JUC-三种等待唤醒方式
Posted 玩葫芦的卷心菜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUC-三种等待唤醒方式相关的知识,希望对你有一定的参考价值。
文章目录
一、synchronized的wait和notify
wait方法会释放对象锁资源后进入等待队列,等待被唤醒
notify会唤醒等待队列中的单个线程
static Object objectLock=new Object();'
new Thread(()->
synchronized (objectLock)
System.out.println(Thread.currentThread().getName()+" come in");
try
objectLock.wait();
catch (InterruptedException e)
e.printStackTrace();
System.out.println(Thread.currentThread().getName()+" 被唤醒");
,"A").start();
new Thread(()->
synchronized (objectLock)
System.out.println(Thread.currentThread().getName()+"唤醒");
objectLock.notify();
,"B").start();
特点:
需要搭配synchronized使用,否则会报异常
需要先等待后唤醒,否则会一直等待
二、Lock的await和signal
await会释放掉锁资源,然后当前线程进入等待队列
signal就是会唤醒等待队列中的单个线程
static Lock lock=new ReentrantLock();
static Condition condition=lock.newCondition();
new Thread(()->
lock.lock();
try
System.out.println(Thread.currentThread().getName() + " come in");
try
condition.await();
catch (InterruptedException e)
e.printStackTrace();
System.out.println(Thread.currentThread().getName() + " 被唤醒");
finally
lock.unlock();
,"A").start();
new Thread(()->
lock.lock();
try
System.out.println(Thread.currentThread().getName() + "唤醒");
condition.signal();
finally
lock.unlock();
,"B").start();
特点:
需要搭配Lock使用,否则会报异常
同样需要先等待后唤醒
三、LookSupport
原理:每个线程都有一个许可证
LookSupport有两个方法
- park:等待/.阻塞,当前线程的许可证-1(消费当前线程许可证),如果许可证为0就阻塞当前线程,只有调用unpark生产许可证供park消费或者线程中断的时候,才会从park返回
- unpark:唤醒,为指定线程生产一个许可证,许可证+1
因为许可证的上限是1,所以多次unpark无效
许可证有两个信号量:1和0
Thread ta=new Thread(()->
// try
// TimeUnit.SECONDS.sleep(1);
// catch (InterruptedException e)
// e.printStackTrace();
//
System.out.println(Thread.currentThread().getName()+" come in");
LockSupport.park();//消费许可证,如果没有就阻塞
System.out.println(Thread.currentThread().getName()+" 被唤醒");
,"A");
ta.start();
new Thread(()->
System.out.println(Thread.currentThread().getName()+"唤醒A");
LockSupport.unpark(ta);//对当前线程的许可证+1,上限为1
,"B").start();
面试题
为什么LockSupport可以先唤醒后等待?
因为唤醒操作unpark生产了许可证
等待操作park可以直接消费掉该许可证,不用阻塞
为什么等待两次唤醒两次,最终线程还是会阻塞?
Thread ta=new Thread(()->
System.out.println(Thread.currentThread().getName()+" come in");
LockSupport.park();//消费许可证,如果没有就阻塞
LockSupport.park();
System.out.println(Thread.currentThread().getName()+" 被唤醒");
,"A");
ta.start();
new Thread(()->
System.out.println(Thread.currentThread().getName()+"唤醒A");
LockSupport.unpark(ta);//对当前线程的许可证+1,上限为1
LockSupport.unpark(ta);
,"B").start();
A线程第一次park,没有许可证阻塞,没有继续执行
线程B连续两次unpark,但是许可证上限为1,所以当前许可证为1
A线程检测到许可证,park消费一个,往下执行,park没有许可证就阻塞了,但是没有线程生产许可证了
特点
不需要在意线程的等待唤醒顺序
不需要维护同步对象和获得锁,实现了线程的解耦
以上是关于JUC-三种等待唤醒方式的主要内容,如果未能解决你的问题,请参考以下文章
JUC - 线程中断与线程等待唤醒(LockSupport)