wait和notify是object中的方法,就是等待和唤醒。一个线程持有锁但是需要等待一些资源,那么就先进入等待队列,其他线程可以先持有锁并执行同步代码块中的内容,等到带着资源的另一个线程抢占到锁,并唤醒等待线程,等待线程就重新进入到阻塞队列进行排队,如果当前无其他线程竞争,就可以直接抢占锁并执行。
wait和notify的基本原理
wait和notify是线程间进行协作的手段,wait和notify使用都先必须获得对象的锁,不获取锁直接用Object对象调用wait和notify,会报错java.lang.IllegalMonitorStateException。
Thread-0先抢占了锁,Owner记录当前抢占锁的线程,因为Thread-0缺少了部分资源调用wait进行等待,进入等待队列,Thread-1同理。当前阻塞队列EntryList中有Thread-3和Thread-4,持有锁的线程是Thread-2,Thread-2中调用了notify就会随机唤醒一个线程,如果唤醒的线程并没有得到需要的资源,那么就会虚假唤醒,因此多个线程等待,我们使用notifyAll,然后搭配while循环判断条件就是Thread-2带来的资源是否是等待线程需要的资源,不是就继续等待,是就进入到EntryList重新排队。
//创建对象
Object lock=new Object();
//.....
//线程A
synchronized(lock){
while(条件不成立){
lock.wait();
}
//说明条件成立,继续执行该线程拿到资源后的工作
}
//...
synchronized(lock){
//做一下处理...完成输送资源的准备
lock.notifyAll();
}
wait和sleep的区别和共同点
- sleep方法属于Thread类,wait属于Object方法
- sleep不用强制与synchronized使用,但wait和synchronized需要一起使用
- sleep在睡眠时不会释放占用对象的锁,wait会释放,性能更高
- 共同点:他们都有状态TIMED—WAITING
同步模式之保护性暂停
保护性暂停,即Guaraded Suspension。用一个线程等待另一个线程的执行结果。
- 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个GuaradedObject
- 如果有结果不断从一个线程到另一个线程那么可以使用消息队列(生产者\消费者)
- JDK中,join、Future的实现,就是用此模式
- 因为要等待另一方的结果,因此归类到同步模式
实现保护性暂停
public class WNTest {
public static void main(String[] args) {
GuardedObject obj=new GuardedObject();
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t"+System.currentTimeMillis()+":想要得到资源");
Object list=obj.get();
System.out.println(Thread.currentThread().getName()+"\t"+System.currentTimeMillis()+":完成任务:"+list);
},"a").start();
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t"+System.currentTimeMillis()+":执行获取资源");
Object list=100;
obj.complete(list);
},"b").start();
}
}
class GuardedObject{
private Object response;
public Object get(){
synchronized (this){
while(response==null){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return response;
}
}
public void complete(Object response){
synchronized (this){
this.response=response;
notifyAll();
}
}
}
增加超时
增加超时效果可以分为三种情况:未超时,成功执行;超时,还没成功获取到结果就跳出循环;防止虚假唤醒
public class WNTest {
public static void main(String[] args) {
GuardedObject obj=new GuardedObject();
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t"+System.currentTimeMillis()+":想要得到资源");
Object list=obj.get(3000);
System.out.println(Thread.currentThread().getName()+"\t"+System.currentTimeMillis()+":完成任务:"+list);
},"a").start();
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t"+System.currentTimeMillis()+":执行获取资源");
Object list=100;
try {
Thread.sleep(3000);//超时的情况
} catch (InterruptedException e) {
e.printStackTrace();
}
obj.complete(list);
},"b").start();
}
}
class GuardedObject{
private Object response;
public Object get(long timeout){
long begint=System.currentTimeMillis();
long passt=0;
synchronized (this){
while(response==null){
long waitt=timeout-passt;
if(passt>=timeout){
//超时跳出循环
break;
}
try {
//为了防止虚假唤醒
this.wait(waitt);
} catch (InterruptedException e) {
e.printStackTrace();
}
passt=System.currentTimeMillis()-begint;
}
return response;
}
}
public void complete(Object response){
synchronized (this){
this.response=response;
notifyAll();
}
}
}
解耦等待和生产
在原本保护性暂停(一个线程等待另一个线程产生的结果),也就是添加了GuardedObject类的基础上,再加了超时处理效果,还可以再加入解耦等待和生产的部分,需要在中间过程添加一个解耦类,解耦类存储的是存放多个GuardedObject对象的集合,也就是解耦了结果生产者和结果消费者。可以同时支持多个任务的管理。
一个线程等待结果一个线程负责给结果,一对一负责,但是可以多个任务管理。
异步模式之生产者\消费者
之所以说生产者\消费者是异步模式,是因为保护性暂停是一个线程等待结果一个线程给结果,是一个负责一个,但在实际情况中,例如送快递,不可能一个快递员负责一个人的快递,那么可以使用生产者\消费者的这种设计。也就是有延迟性,不同生产结果和消费结果的线程一一对应。
那么这样的设计主要是通过消费队列来实现,生产者\消费者中的消息队列与RabbitMQ中的消息队列是不同的,生产者\消费者的是Java线程间的通信,而RabbitMQ中是进程间的通信。
- 消费队列可以来平衡生产和消费的线程资源
- 生产者仅负责生产结果数据,不管数据是如何处理的,而消费者只负责产生结果数据。
- 消息队列是有容量限制的,满的时候不能再生产消息,空时不能消费消息。
- 与保护行暂停的设计不同,生产者\消费者是不同生产结果和消费结果的线程一一对应。
- JDK中许多阻塞队列就采用的是这种方式。
消息队列采用的是双端队列,从头取出,从位传入,主要是遵循FIFO原则。
class MessageQueue{
//消息队列的集合
private LinkedList<Message> messageList=new LinkedList<>();
//消息队列的容量
private int capcity;
public MessageQueue(int capcity) {
this.capcity = capcity;
}
public Message take(){
synchronized (messageList){
//消息队列空则等待
while(messageList.isEmpty()){
try {
messageList.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Message message=messageList.removeFirst();
messageList.notifyAll();
return message;
}
}
public void put(Message message){
synchronized (messageList){
//消息队列满,则等待
while(messageList.size()==capcity){
try {
messageList.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
messageList.addLast(message);
messageList.notifyAll();
}
}
}
final class Message{
//消息的id
private int id;
//消息的值
private Object value;
/**
*只保留读操作,且不可继承此类,保证了该类的线程安全
*/
public Message(int id, Object value) {
this.id = id;
this.value = value;
}
public int getId() {
return id;
}
public Object getValue() {
return value;
}
@Override
public String toString() {
return "Message{" +
"id=" + id +
", value=" + value +
'}';
}
}
class GuardedObject{
private Object response;
public Object get(long timeout){
long begint=System.currentTimeMillis();
long passt=0;
synchronized (this){
while(response==null){
long waitt=timeout-passt;
if(passt>=timeout){
break;
}
try {
this.wait(waitt);
} catch (InterruptedException e) {
e.printStackTrace();
}
passt=System.currentTimeMillis()-begint;
}
return response;
}
}
public void complete(Object response){
synchronized (this){
this.response=response;
notifyAll();
}
}
}