其他分享
首页 > 其他分享> > 话说 LockSupport

话说 LockSupport

作者:互联网

LockSupport

LockSupport要从wait/notify/notifyAll 、condition.await/signal/signalAll 说起

在JUC包中很多地方都会使用到LockSupport 比如我们前边写的ReentrantLock中,获取锁失败之后会加入队列调用LockSupport.park() 等待前边一个获取锁的线程unpark自己

下边以小强和小月月用同一个水杯喝水为例 讲解wait/notify 、await/signal 、park/unpark

一、 wait notify

wait nofity 的详细讲解 前边文章有写过 这里只是简单实用 仅为了与LockSupport比较

class TestWaitNotify {public Object obj = new Object();public static void main(String[] args) {TestWaitNotify test = new TestWaitNotify();new Thread(test::xiaoqiang).start();new Thread(test::xiaoyueyue).start();}/**
     * 小强喝水
     */public void xiaoqiang(){synchronized (obj){while (true){try{System.out.println("小强喝水");//通知别人喝水obj.notify();// 自己waitobj.wait();} catch (Exception e){e.printStackTrace();}}}}/**
     * 小月月喝水
     */public void xiaoyueyue(){synchronized (obj){while (true){try{System.out.println("小月月喝水");//通知别人喝水obj.notify();// 自己waitobj.wait();} catch (Exception e){e.printStackTrace();}}}}}输出结果:
小强喝水
小月月喝水
小强喝水
小月月喝水
小强喝水
小月月喝水...

小强喝完了通知别人可以喝了,然后自己等着

小月月收到通知,喝水,喝完了通知别人可以喝了,然后自己等着

如此循环往复…

wait/notify/notifyAll 还是老生常谈的问题:

1. 必须在synchronized代码块或者synchronized修饰的方法内  否则会报异常:java.lang.IllegalMonitorStateException
2. nofity只有在wait方法调用之后调用才能生效  先调用notify再调用wait 没用。。。
3. 线程interrupt 中断会打断wait

二、await signal
class TestAwaitSignal {Lock lock = new ReentrantLock();Condition cd = lock.newCondition();public static void main(String[] args) {TestAwaitSignal test = new TestAwaitSignal();new Thread(test::xiaoqiang).start();new Thread(test::xiaoyueyue).start();}/**
     * 小强喝水
     */public void xiaoqiang(){try {lock.lock();while (true){try{System.out.println("小强喝水");//signal通知别人喝水cd.signal();// 自己awaitcd.await();} catch (Exception e){e.printStackTrace();}}} catch (Exception e){e.printStackTrace();} finally {lock.unlock();}}/**
     * 小月月喝水
     */public void xiaoyueyue(){try {lock.lock();while (true){try{System.out.println("小月月喝水");//signal通知别人喝水cd.signal();// 自己awaitcd.await();} catch (Exception e){e.printStackTrace();}}} catch (Exception e){e.printStackTrace();} finally {lock.unlock();}}}输出结果:
小强喝水
小月月喝水
小强喝水
小月月喝水
小强喝水
小月月喝水
小强喝水
小月月喝水
小强喝水...

await/signal/signalAll还是老生常谈的问题:

1. 必须在lock获取锁代码块中执行 否则会报IllegalMonitorStateException异常
2. lock必须在finally中手动释放锁
3. signal 必须 在await之后调用  否则不起作用 
4. condition.await比wait多可中断、到达指定时(前边文章有写)间停止等功能(前边文章有写)
5. 一个lock可以new多个互不干扰的condition (前边文章有写)

三、LockSupport

3.1 使用

这是要给很好的东西

park /pɑːk/ 停车场、停放

unpark 把车从停车场离开

从一个故事走进park/unpark ,小强开着车走进一个停车场park了一下,然后他就出不去了,只能一直在停车场停着,直到小月月给他送来停车券unpark,这时候他才能出去

停车例子:

class TestParkUnpark {public static void main(String[] args) throws InterruptedException {TestParkUnpark test = new TestParkUnpark();Thread thread = new Thread(test::xiaoqiang);thread.start();Thread.sleep(5000);// 小月月给停车券new Thread(()->{test.xiaoyueyue(thread);}).start();}/**
     * 小强进入停车场
     */public void xiaoqiang(){System.out.println("停车等着出停车场");LockSupport.park();System.out.println("我出来了!!");}/**
     * 小月月给停车券
     */public void xiaoyueyue(Thread thread){System.out.println("给小强那二傻子送个停车券");LockSupport.unpark(thread);}}输出:
停车等着出停车场
给小强那二傻子送个停车券
我出来了!!

很明显看出来了,

1.LockSupport 与wait/condition.await最大的区别就是 他不要锁,不需要在锁代码块中使用

3.2 不分顺序

LockSupport还有一个特点就是unpark可以先于park执行,就是小强这二傻子去停车场之前,小月月先把停车券给他了unpark,等他想走的时候手里有停车券不需要再一次获取停车券unpark了。

class TestParkUnpark02 {public static void main(String[] args) throws InterruptedException {TestParkUnpark02 test = new TestParkUnpark02();Thread thread = new Thread(test::xiaoqiang);// 进入停车场thread.start();// 小月月先给停车券new Thread(()->{test.xiaoyueyue(thread);}).start();}/**
     * 小强进入停车场
     */public void xiaoqiang()   {try {// 在外边潇洒呢Thread.sleep(10000);// 进停车场了System.out.println("停车等着出停车场");LockSupport.park();System.out.println("我出来了!!");} catch (Exception e){}}/**
     * 小月月给停车券
     */public void xiaoyueyue(Thread thread){System.out.println("给小强那二傻子送个停车券");LockSupport.unpark(thread);}}输出结果:
给小强那二傻子送个停车券
停车等着出停车场
我出来了!!

3.3 可中断 可超时返回 可指定等待时间点返回
class TestParkUnpark03 {public static void main(String[] args)   {TestParkUnpark03 test = new TestParkUnpark03();// 可中断 停止parkThread thread = new Thread(test::xiaoqiang);thread.start();thread.interrupt();}/**
     *  可中断
     */public void xiaoqiang()   {// 进停车场了System.out.println("停车等着出停车场");LockSupport.park();boolean interrupted = Thread.currentThread().isInterrupted();if (interrupted){System.out.println("被中断的 强行闯卡");} else {System.out.println("拿到票 我出来了!!");}}}

class TestParkUnpark04 {public static void main(String[] args)   {TestParkUnpark04 test = new TestParkUnpark04();// 可超时Thread thread = new Thread(test::xiaoqiang);thread.start();}/**
     *  可超时
     */public void xiaoqiang()   {// 进停车场了System.out.println("停车等着出停车场");// 5秒后自己出去long nanos = TimeUnit.SECONDS.toNanos(5);LockSupport.parkNanos(nanos);System.out.println("结束park 要么是给了券  要么是小强跟门卫耗了半天让他出去了");}}

class TestParkUnpark04 {public static void main(String[] args)   {TestParkUnpark04 test = new TestParkUnpark04();// 可指定等待时间点 到这个点就停止park Thread thread = new Thread(test::xiaoqiang);thread.start();}/**
     *  可指定等待时间点 到这个点就停止park 
     */public void xiaoqiang()   {// 进停车场了System.out.println("停车等着出停车场");// 10秒后自己出去long nanos = TimeUnit.SECONDS.toMillis(10);Date date= new Date(System.currentTimeMillis()+nanos);// 到指定时间就停止LockSupport.parkUntil(date.getTime());System.out.println("结束park 要么是给了券  要么是小强跟门卫耗了半天让他出去了  要么是到了晚上(指定时间)门卫不在 自己溜出去了");}}

3.4还有一个 功能

这个功能乍一看没啥作用。

class TestParkUnpark04 {public static void main(String[] args)   {TestParkUnpark04 test = new TestParkUnpark04();new Thread(test::xiaoqiang01,"线程001").start();new Thread(test::xiaoqiang02,"线程002").start();}public void xiaoqiang01()   {// 这里传入了一个对象LockSupport.park();}public void xiaoqiang02()   {// 这里传入了一个对象LockSupport.park(new ParkParam());}}class ParkParam{}

我们用jps找到当前java进程,再用jstack查看堆栈信息

1. 打开cmd
2. jps
	11672 TestParkUnpark04
3. jstack -l 11672
部分输出内容: 
"线程002" #12 prio=5 os_prio=0 tid=0x000000001e042800 nid=0x263c waiting on condition [0x000000001eaff000]
   java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for  <0x000000076bbb1a20> (a vip.freeedu.ParkParam)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)at vip.freeedu.TestParkUnpark04.xiaoqiang02(TestParkUnpark04.java:25)at vip.freeedu.TestParkUnpark04$$Lambda$2/990368553.run(Unknown Source)at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
        - None"线程001" #11 prio=5 os_prio=0 tid=0x000000001e03f800 nid=0x25fc waiting on condition [0x000000001e9ff000]
   java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:304)at vip.freeedu.TestParkUnpark04.xiaoqiang01(TestParkUnpark04.java:21)at vip.freeedu.TestParkUnpark04$$Lambda$1/2003749087.run(Unknown Source)at java.lang.Thread.run(Thread.java:748)

在这里插入图片描述

可以看到线程002 多了一行 -parking for wait for xxxx

这样我们在排查问题的时候就容易知道是哪儿出了问题 ,每个park处都new不一样的对象,这样很容易定位

3.5 LockSupport**优点出来了
1. 不用与锁混合使用!! 
2. 可以先unpark拿到停车券 在park的时候就可以直接用停车券通过!!
3. 可以超时停止等待 ; wait await都支持
4. 可停止到指定时间 ;await支持
5. 可中断 ;wait await都支持  await支持不响应中断

这里说一点哈 ,Object的wait也支持wait超时停止等待

3.6 注意:unpark不能累计

park和unpark的实现原理。。 我看了一下hotspot的实现 没有看懂 ,但是我看注释意思可能就是这样的,

有一个标志位**_event** ,

3.7 park

当调用park的时候就会判断**_event**值

  1. 如果是-1 非法数据

  2. 如果是1 那就设置为0 返回(不阻塞直接过)相当于手持停车券 出停车场 顺畅无阻 (先unpark 再 park )

  3. 如果是0 那就把**_event**设置为-1

void os::PlatformEvent::park() {       // AKA "down()"
  // Transitions for _event:
  //   -1 => -1 : illegal
  //    1 =>  0 : pass - return immediately
  //    0 => -1 : block; then set _event to 0 before returning

在这里插入图片描述

3.8 unpark

当调用unpark****的时候就会判断_event**值

  1. 如果是0,设置**_event**为1 这就是为什么unpark可以在park之前调用,因为这里设置为1 然后park的时候就会用这个值

  2. 如果是1 返回 ,不会累积 这就是为什么unpark多次只能park使用一次

  3. 如果是-1 那就把**_event**设置为0 或 1 ,唤醒目标线程

void os::PlatformEvent::unpark() {
  // Transitions for _event:
  //    0 => 1 : just return 
  //    1 => 1 : just return
  //   -1 => either 0 or 1; must signal target thread
  //         That is, we can safely transition _event from -1 to either
  //         0 or 1.

unpark***的时候就会判断**_event**值

  1. 如果是0,设置**_event**为1 这就是为什么unpark可以在park之前调用,因为这里设置为1 然后park的时候就会用这个值

  2. 如果是1 返回 ,不会累积 这就是为什么unpark多次只能park使用一次

  3. 如果是-1 那就把**_event**设置为0 或 1 ,唤醒目标线程

void os::PlatformEvent::unpark() {
  // Transitions for _event:
  //    0 => 1 : just return 
  //    1 => 1 : just return
  //   -1 => either 0 or 1; must signal target thread
  //         That is, we can safely transition _event from -1 to either
  //         0 or 1.

在这里插入图片描述


标签:Thread,void,park,LockSupport,unpark,new,public
来源: https://blog.51cto.com/u_12198094/2713572