wait和notify基本原理以及应用场景

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();
         }
    }
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值