并发编程之线程间的通信

经典消费者,生产者案例(不同于操作系统的生消案例)

public class Producer implements Runnable {
    private Queue<String> bags;
    private int maxSize;

    public Producer(Queue<String> bags, int maxSize) {
        this.bags = bags;
        this.maxSize = maxSize;
    }

    @Override
    public void run() {
        int i = 0;
        while (true) {
            i++;
            synchronized (bags) { //抢占锁
                if (bags.size() == maxSize) {
                    System.out.println("bags 满了");
                    try {
                        bags.wait(); 
                        //满了,阻塞当前线程加入到等待队列中并且释放锁,不等于同步代码块的结束
                        //wait底层实现是park(); ->JVM ->Native
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("生产者生产:bag" + i);
                bags.add("bag" + i); //生产bag
                bags.notify(); //表示当前已经生产了数据,提示消费者可以消费了 
                //把等待队列的一个线程移动到同步队列中,开始尝试抢占锁
                
            }//同步代码快执行结束
        }
    }
}
public class Consumer implements Runnable{
    private Queue<String> bags;
    private int maxSize;

    public Consumer(Queue<String> bags, int maxSize) {
        this.bags = bags;
        this.maxSize = maxSize;
    }

    @Override public void run() {
        while(true){
            synchronized (bags){
                if(bags.isEmpty()){
                    System.out.println("bags为空");
                    try {
                        bags.wait();//同理
                    }  catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                try {
                    Thread.sleep(1000);
                }  catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String bag=bags.remove();
                System.out.println("消费者消费:"+bag);
                bags.notify(); //这里只是唤醒等待队列中的线程,移动至同步队列中
                //但是Producer线程并不能马上执行,因为同步代码块还没执行完,monitorexit指令也未执行完成
            } //同步代码块执行结束, monitorexit指令执行完成,Producer才有可能运行
       }
    }
}

Wait和Notify的流程

在这里插入图片描述
注意

  • wait是把当前线程释放锁加入到等待队列
  • notify是把等待队列的一个等待线程移动到同步队列中。
  • 同步队列是一直在抢占锁

Join

当前线程调用,则其它线程全部停止,等待当前线程执行完毕,接着执行。

不难想到也是利用了通信机制,当前线程执行完发出一个信号其它线程再执行

public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

没看到notify?这个notify是在JVM层面实现的

static void ensure_join(JavaThread* thread) { 

Handle threadObj(thread, thread->threadObj()); 
assert(threadObj.not_null(), "java thread object must exist"); 
ObjectLocker lock(threadObj, thread);

thread->clear_pending_exception(); 
 java_lang_Thread::set_thread_status(threadObj(), 
 java_lang_Thread::TERMINATED);  
  java_lang_Thread::set_thread(threadObj(), NULL); 

lock.notify_all(thread); 

 thread->clear_pending_exception(); }

线程执行完会调用一个JVM的清理(exit)方法,然后会调用ensure_join方法这个方法倒数第二行即notifyall

Condition案例

Condition等价于Wait和Notify(是J.U.C包中实现的,跟Lock配套

  • J.U.C层面中,锁的实现是Lock
  • wiat/notify,锁的实现是synchronized
public class ConditionDemoWait implements Runnable {
    private Lock lock;
    private Condition condition;

    public ConditionDemoWait(Lock lock, Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }

    @Override public void run() {
        System.out.println("begin - ConditionDemoWait");
        lock.lock();
        try{
            condition.await(); //让当前线程阻塞,Object.wait();
            System.out.println("end - ConditionDemoWait");
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}
public class ConditionDemeNotify implements Runnable{
    private Lock lock;
    private Condition condition;

    public ConditionDemeNotify(Lock lock, Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }

    @Override
    public void run() {
        System.out.println("begin - ConditionDemeNotify");
        lock.lock(); //synchronized(lock)
         try{
             condition.signal(); //让当前线程唤醒 Object.notify(); //因为任何对象都会 有monitor
             //继续执行,因为signal只是唤醒,还抢不到锁,因为此线程还未释放锁
             System.out.println("end - ConditionDemeNotify");
         }catch (Exception e){
             e.printStackTrace();
         }finally {
             lock.unlock();
         }
    }
}
public class ConditionExample {
    public static void main(String[] args) {
        Lock lock=new ReentrantLock();
        Condition condition=lock.newCondition();
        ConditionDemoWait cd=new ConditionDemoWait(lock,condition);
        ConditionDemeNotify conditionDemeNotify=new ConditionDemeNotify(lock,condition);

        new Thread(cd).start();
        new Thread(conditionDemeNotify).start();
    }
}

在这里插入图片描述

要点

  • signal不会释放锁,await会释放锁
  • 原理和wait/notify一样

Condition设计猜想

  • 作用: 实现线程的阻塞和唤醒
  • 前提条件: 必须先要获得锁
    • await/signal/signalAll
      - await 让线程阻塞, 并且释放锁
      - signal -> 唤醒阻塞的线程
  • 加锁的操作,必然会涉及到AQS的阻塞队列(就是以前说的AQS的队列),未成功就放入阻塞队列。
  • await 释放锁的时候, AQS队列中不存在已经释放锁的线程,这个被释放的线程一定存放在了conditon的等待队列,类似于wait的等待队列,也称条件队列
  • signal 唤醒被阻塞的线程 从条件队列中,再次放入AQS队列。(AQS阻塞队列相当于wait/notify的同步队列)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值