线程间通讯 :
多个线程在处理同一个资源的时候,当线程处理的任务不同。默认的情况下cpu是在随机切换线程,那你邮箱多个线程可以有规律的执行,那么多线程之间需要一些协调通信;
需要线程通信来帮助解决线程之间对同一个变量的使用或操作,就是多个线程在操作同一份数据时,避免对同一共享变量的争夺。也就是我们需要通过一定的手段使各个线程能有效地利用资源,这种手段即——等待唤醒机制。
比如我们实现实现一个线程对该变量加1,一个线程对该变量减1,* 交替,来10轮。
这个就是典型的生产者+消费者+通知等待唤醒机制
使用synchronized方式实现:
- 1:判断
- 2:干活
- 3:通知
资源类ShartDataOne:
class ShartDataOne {
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition cd = lock.newCondition();
public synchronized void increment() throws InterruptedException
{
//1判断
if(number !=0 ) {
this.wait();
}
//2干活
++number;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//3通知
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException
{
// 1判断
if (number == 0) {
this.wait();
}
// 2干活
--number;
System.out.println(Thread.currentThread().getName() + "\t" + number);
// 3通知
this.notifyAll();
}
}
#当只有两个线程的时候,即A1线程负责+操作,B1负责-操作
此时运行结果都是顺序执行:
但是当我们增加到4个线程的时候,A1,A2负责+,B1,B2负责-
此时的运行结果却不是我们的预期:
上面的结果就出现了虚假唤醒的结果:
为什么换成四个线程就出现了虚假唤醒
问题就是出现在我们的判断上使用了if,为什么使用了if会出现这种问题呢?
- 1: 首先我们在方法上加入了synchronized代表这是一个同步方法,如果这个对象有多个synchronized方法的话,当一个线程进入了其中一个,其他线程就只能等待这个线程释放锁,才能访问其他的synchronized方法;因为在方法上加入关键字synchronized,锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
换成两个对象后,不是同一把锁了,情况立刻变化。 - 2: 我们在主方法中都是调用同一个对象实例,所以上面的讲的也就是为什么一开始两个线程能成功的原因,但是换成四个就不能成功?
- 3:因为if判断只能判断一次,例如:默认都是0,如果B1先进来,if判断了(number==0)=true,那么B1就会this.wait(),交出锁。默认的情况下cpu是在随机切换线程;接下来如果B1再次抢到锁,进入后并不是执行判断if,而是从wait()后继续执行,也就是–,所以此时的number就是-1,。。。。。。
那有上面的办法解决呢
就是将判断if换成while,比如上面举的例子,如果使用了while,B1在次抢到锁,还得进行while判断,继续wait();
如果使用JUC中的Lock来实现呢?
使用Lock实现
类比sychronized方式:
使用Lock实现:对标wait与notify的是Condition: await和signal
在Lock接口上可以看到:
Lock的实现类都可以返回个Condition对象
Condition
- Condition factors out the Object monitor methods (wait, notify and notifyAll) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary Lock implementations. Where a Lock replaces the use of synchronized methods and statements, a Condition replaces the use of the Object monitor methods.
- 将((wait, notify and notifyAll)分解为对象,也就是Condition
资源类:
class ShartDataOne {
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition cd = lock.newCondition();
/**
* 同步方法 使number++
*
* @Author:LRC
* @Date:11:17 上午 2020/7/3
*/
public void increment() throws InterruptedException {
lock.lock();
try {
while (number != 0) {
cd.await();
}
++number;
System.out.println(Thread.currentThread().getName() + "\t" + number);
//通知
cd.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
/**
* 同步方法 使number--
*
* @Author:LRC
* @Date:11:18 上午 2020/7/3
*/
public void decrement() throws InterruptedException {
lock.lock();
try {
//判断
while (number != 1) {
cd.await();
}
--number;
System.out.println(Thread.currentThread().getName() + "\t" + number);
//通知
cd.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
同样是4个线程没出错
实现线程通讯注意在判断上不要使用if
将synchronized方式慢慢转变成使用lock