1、线程和进程
-
Java默认有几个线程?
2个----mian和GC
-
Java真的可以开启线程吗?
开不了
public synchronized void start() { // 本地方法,底层的C++ ,Java 无法直接操作硬件 private native void start0(); }
-
线程的几个状态
public enum State { // 新生 NEW, // 运行 RUNNABLE, // 阻塞 BLOCKED, // 等待,死死地等 WAITING, // 超时等待 TIMED_WAITING, // 终止 TERMINATED; }
-
wait和sleep的区别
-
来自不同的类
wait–>Object
sleep–>Thread
-
wait会释放锁;而sleep不会释放,抱着锁睡觉
-
使用的范围不同
扫描二维码关注公众号,回复: 12911401 查看本文章wait必须在同步代码块中
sleep可以在任何地方
-
2、Lock锁(重点)
2.1、传统的Synchronized
// 资源 OOP
class Ticket {
// 属性+方法
private int number = 30;
public synchronized void sale() {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "买到了第" + number-- +"张票");
}
}
}
/*
真正的多线程开发,公司中的开发,降低耦合性
线程就是一个单独的资源类,没有任何附属的操作!
*/
public class TestSynchronized {
public static void main(String[] args) {
// 并发:多线程操作同一个资源类,把资源丢入线程
Ticket ticket = new Ticket();
new Thread(() -> {
for (int i = 1; i < 20; i++) {
ticket.sale();
}
}, "小明").start();
new Thread(() -> {
for (int i = 1; i < 20; i++) {
ticket.sale();
}
}, "小红").start();
new Thread(() -> {
for (int i = 1; i < 20; i++) {
ticket.sale();
}
}, "黄牛").start();
}
}
2.2、Lock 接口
class Ticket_Lock {
private int number = 30;
Lock lock = new ReentrantLock();
public void sale() {
lock.lock();
try {
if (number > 0){
System.out.println(Thread.currentThread().getName() + "买到了第" + number-- +"张票");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class TestLock {
public static void main(String[] args) {
Ticket_Lock ticket = new Ticket_Lock();
new Thread(()->{
for (int i = 1; i < 20 ; i++)
ticket.sale();},"A").start();
new Thread(()->{
for (int i = 1; i < 20 ; i++)
ticket.sale();},"B").start();
new Thread(()->{
for (int i = 1; i < 20 ; i++)
ticket.sale();},"C").start();
}
}
2.3、Synchronized 和 Lock 的区别
- Synchronized 内置的Java关键字, Lock 是一个Java类
- Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
- Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
- Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下去;
- Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以自己设置);
- Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!
3、生产者和消费者问题
3.1、Synchronized版
class Data{
private int number = 0;
public synchronized void increment() throws InterruptedException {
while (number != 0) {
//不能用if进行判断,原因见下
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "===>" + number);
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while (number == 0) {
//不能用if进行判断
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "===>" + number);
this.notifyAll();
}
}
public class TestPV {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}
- 用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。
3.2、JUC版
class Data2{
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
while (number != 0) {
condition.await(); //从wait变为await
}
number++;
System.out.println(Thread.currentThread().getName() + "===>" + number);
condition.signalAll(); //从notifyAll变为signalAll
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while (number == 0) {
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "===>" + number);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class TestCondition {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}
3.3、通过Condition精准唤醒
class Data3{
private int number = 1;
Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
public void printA() {
lock.lock();
try {
while (number != 1) {
conditionA.await();
}
System.out.println(Thread.currentThread().getName() + "AAAAAA");
number = 2;
conditionB.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
while (number != 2) {
conditionB.await();
}
System.out.println(Thread.currentThread().getName() + "BBBBBB");
number = 3;
conditionC.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
while (number != 3) {
conditionC.await();
}
System.out.println(Thread.currentThread().getName() + "CCCCCC");
number = 1;
conditionA.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class TestCondition2 {
public static void main(String[] args) {
Data3 data = new Data3();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.printA();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.printB();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.printC();
}
}, "C").start();
}
}
4、八锁问题—助你全面理解锁机制
4.1、多个线程使用同一把锁—顺序执行
class Phone1 {
public synchronized void sendEmail() {
System.out.println("发短信");
}
public synchronized void callPhone() {
System.out.println("打电话");
}
}
public class LockDemo1 {
public static void main(String[] args) {
Phone1 phone1 = new Phone1();
new Thread(() -> {
phone1.sendEmail();}, "A").start();
new Thread(() -> {
phone1.callPhone();}, "B").start();
} // 发短信-->打电话
}
- 解释:被 synchronized 修饰的方法,锁的对象是方法的调用者,两个方法调用的对象是同一个,先获得锁的先执行,也就是顺序执行。
4.2、多个线程使用同一把锁,其中某个线程里面还有阻塞—顺序执行
class Phone2 {
public synchronized void sendEmail() {
System.out.println("发短信");
}
public synchronized void callPhone() {
System.out.println("打电话");
}
}
public class LockDemo2 {
public static void main(String[] args) throws InterruptedException {
Phone2 phone2 = new Phone2();
new Thread(() -> {
phone2.sendEmail();}, "A").start();
TimeUnit.SECONDS.sleep(2);
new Thread(() -> {
phone2.callPhone();}, "B").start();
} // 发短信-->打电话
}
- 解释:锁的对象还是方法的调用者,两个方法调用的对象是同一个,先调用的先执行!
4.3、多个线程有锁与没锁—随机执行
class Phone3 {
public synchronized void sendEmail() {
System.out.println("发短信");
}
public void callPhone() {
System.out.println("打电话");
}
}
public class LockDemo3 {
public static void main(String[] args) throws InterruptedException {
Phone3 phone3 = new Phone3();
new Thread(() -> {
phone3.sendEmail();}, "A").start();
new Thread(() -> {
phone3.callPhone();}, "B").start();
} //随机执行
}
- 解释:同步方法有锁机制,而普通方法没有 synchronized 修饰,不受锁的影响!
4.4、多个线程使用多把锁—随机执行
class Phone4 {
public synchronized void sendEmail() {
System.out.println("发短信");
}
public synchronized void callPhone() {
System.out.println("打电话");
}
}
public class LockDemo4 {
public static void main(String[] args) throws InterruptedException {
Phone4 phoneA = new Phone4();
Phone4 phoneB = new Phone4();
new Thread(() -> {
phoneA.sendEmail();}, "A").start();
new Thread(() -> {
phoneB.callPhone();}, "B").start();
} //随机执行
}
- 解释:被 synchronized 修饰的方法,锁的对象是调用者,这里的锁是两个不同的对象,也就是有两个不同的资源,所以互不影响。
4.5、Class锁:多个线程使用一个对象—顺序执行
-
被 synchronized 修饰的方法,锁的对象是调用者;
-
被 synchronized 和 static 同时修饰的方法,锁的对象是类的 class 对象,是唯一的一把锁。
锁Class和锁对象的区别:
- Class 锁 ,类模版,只有一个;
- 对象锁 , 通过类模板可以new 多个对象。
class Phone5 {
public static synchronized void sendEmail() {
System.out.println("发短信");
}
public static synchronized void callPhone() {
System.out.println("打电话");
}
}
public class LockDemo5 {
public static void main(String[] args) throws InterruptedException {
Phone5 phone5 = new Phone5();
new Thread(() -> {
phone5.sendEmail();}, "A").start();
new Thread(() -> {
phone5.callPhone();}, "B").start();
} // 发短信-->打电话
}
- 解释:两个方法都同时被 synchronized 和 static 修饰,锁的对象就是 Class 模板对象,全局唯一!两个线程争抢同一个锁,并不是因为 synchronized 导致的,程序会从上到下依次执行。
4.6、Class锁:多个线程使用多个对象—顺序执行
class Phone6 {
public static synchronized void sendEmail() {
System.out.println("发短信");
}
public static synchronized void callPhone() {
System.out.println("打电话");
}
}
public class LockDemo6 {
public static void main(String[] args) throws InterruptedException {
Phone6 phoneA = new Phone6();
Phone6 phoneB = new Phone6();
new Thread(() -> {
phoneA.sendEmail();}, "A").start();
new Thread(() -> {
phoneB.callPhone();}, "B").start();
} // 发短信-->打电话
}
- 解释:被 synchronized 修饰 和 static 修饰的方法,锁的对象是类的 class 对象,是唯一的一把锁。
Class锁是唯一的,所以多个对象使用的也是同一个Class锁。
4.7、Class锁与对象锁:多个线程使用一个对象—随机执行
class Phone7 {
public static synchronized void sendEmail() {
System.out.println("发短信");
}
public synchronized void callPhone() {
System.out.println("打电话");
}
}
public class LockDemo7 {
public static void main(String[] args) throws InterruptedException {
Phone7 phone7 = new Phone7();
new Thread(() -> {
phone7.sendEmail();}, "A").start();
new Thread(() -> {
phone7.callPhone();}, "B").start();
} //随机执行
}
- 解释:被 synchronized和static修饰的方法,锁的对象是类的class对象!唯一的同一把锁;只被synchronized修饰的方法,是普通锁(如对象锁),不是Class锁,所以进程之间执行顺序互不干扰。
4.8、Class锁与对象锁:多个线程使用多个对象—随机执行
class Phone8 {
public static synchronized void sendEmail() {
System.out.println("发短信");
}
public synchronized void callPhone() {
System.out.println("打电话");
}
}
public class LockDemo8 {
public static void main(String[] args) throws InterruptedException {
Phone8 phoneA = new Phone8();
Phone8 phoneB = new Phone8();
new Thread(() -> {
phoneA.sendEmail();}, "A").start();
new Thread(() -> {
phoneB.callPhone();}, "B").start();
} //随机执行
}
- 解释:被 synchronized和static修饰的方法,锁的对象是类的class对象!唯一的同一把锁;只被synchronized修饰的方法,是普通锁(如对象锁),不是Class锁,所以进程之间执行顺序互不干扰。
小结
- new this 本身的这个对象,调用者
- static class 类模板,保证唯一!
一个对象中含有多个synchronized方法,当一个线程去访问synchronized修饰的方法时就会加锁,其他线程就会被阻塞;就像一个独立的公共厕所,只允许一个人进去。
静态同步方法锁的是Class对象,所有的静态同步方法的锁唯一,就算有多个线程访问多个资源也会被阻塞,因为这些资源都来自同一个Class对象,而Class对象唯一!