线程的通信

可以在synchronized关键字下使用:

使用wait/notify线程通信:

wait/notify的原理如下

锁对象.wait():让该线程进入MonitorWaitSet中,并释放当前锁。调用此方法后,当前线程将释放对象监控权 ,然后进入等待WAITING状态。在当前线程被notify后会立刻进入MonitorEntryList中处于BLOCLED状态,等待锁的释放然后重新竞争锁,竞争成功后从断点处继续代码的执行。

用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码;而如果使用while的话,也会从wait之后的代码运行,但是唤醒后重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait

 

sleepwait方法的异同:

相同:1、两种方法都能造成线程的阻塞。

不同:1sleep方法原始定义在Thread类中,wait方法原始定义在Object类中;

   2sleep方法可以在任何位置使用,wait方法只能使用在同步代码块和同步方法中(只能配合synchronized);

  3sleep方法不会释放锁,wait方法会释放锁;

  4sleep方法只能阻塞有限时间,wait方法可以一直阻塞。

 

锁对象.notify():唤醒随机一个WaitSet的线程,该方法存在虚假唤醒,即唤醒线程后发现条件不满足。

锁对象.notifyAll():唤醒所有WaitSet中的线程,一般都用这个方法

这三种方法只能在synchronized同步代码块同步方法)中使用;

▲notify()和notifyAll()调用后要等到当前线程走完synchronized释放锁后才能让被唤醒线程开始争夺

三种方法只能由当前同步代码块或同步方法中的“”来调用;

▲因为这三个方法必须有锁对象调用,而任意对象都可以作为synchronized的同步锁,因此这三个方法只能在Object类中声明。

 

线程通信使用模板:

synchronized(lock) {

while(条件不成立) {

lock.wait();

}

// 干活

}

//另一个线程

synchronized(lock) {

lock.notifyAll();

}

 

保护性暂停

用在一个线程等待另一个线程的一个执行结果,相当于简化版的生产者消费者模型;

有一个结果需要从一个线程传递到另一个线程,让他们关联同一个GuardedObject,如果有结果不断从一个线程到另一个线程那么可以使用消息队列(见生产者/消费者);

JDK 中,join 的实现、Future 的实现,采用的就是此模式。因为要等待另一方的结果,因此归类到同步模式。

 

保护性暂停的模板:

线程间要返回的用于线程通信的结果数据设置成成员变量,生成存取方法,两方法通过wait/notifyAll通信。

 

异步模式之生产者消费者模式:

与前面的保护性暂停中的 GuardObject 不同,不需要产生结果和消费结果的线程一一对应

消费队列可以用来平衡生产和消费的线程资源。生产者仅负责产生结果数据,不关心数据该如何处理,而消费者专心处理结果数据;

消息队列是有容量限制的,满时不会再加入数据,空时不会再消耗数据;

JDK 中各种阻塞队列,采用的就是这种模式 。因为不需要等待消费者线程返回的值,只用专注生产,所以异步。

 

 

消息队列模板(理论上只用创造这一个类就行,分别在两个线程里使用存取方法即可)

把消息队列看成成员变量list,把要存取的元素看成list的元素,生成存取方法,互相wait/notifyAll通信。

 

Lock接口实现类下的线程通信:

Condition.await()底层是调用LockSupport.park()来实现阻塞当前线程的。

await/signal

可以用同一个锁创建多个休息室,这里的休息室不是synchronizedWaitset,而是conditionObject,对不同的线程调用await方法让他们进入不同的休息室 。其他用法和wait/notify相同

ReentrantLock lock = new ReentrantLock();

Condition condition = lock.newCondition();

condition.await():在线程中调用这个方法表明让线程进入这个休息室,用法和wait()相同。

condition.signal():用法和notify()相同。

condition.signalAll():用法和notifyAll()相同。

 

 

 

使用LockSupport类中的parkunpark方法,但是LockSupport中的park/unpark方法底层使用的是Unsafe类的

park/unpark方法:

park/unpark的原理如下

LockSupport.park()

每个线程都有自己的一个 Parker 对象,内部由三属性组成 _counter、 _condition 条件变量 和 _mutex互斥锁

当线程调用 park() 方法时,先检查 _counter,如果为 0,则获得 _mutex锁 然后进入 _cond 阻塞,最后设置 _counter = 0;如果为1,则不进入阻塞接着运行,最后设置counter0

park方法不会释放锁。

LockSupport.unpark(线程对象)

parkunpark

park会让线程阻塞,此时counter属性为0unpark时先把counter置为1,再唤醒线程,释放互斥锁,最后counter=0

unparkpark

先把counter置为1,此时没有线程唤醒,park时先检查counter,此时为1,这时不进入阻塞继续运行,最后将counter置为0

 

和wait/notify相比的特点:

1、可以先也可以后

2、不需要用锁或condition调用

3、阻塞和唤醒时都是针对某个线程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值