关于java:话说-LockSupport

47次阅读

共计 7134 个字符,预计需要花费 18 分钟才能阅读完成。

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();
                    // 本人 wait
                    obj.wait();} catch (Exception e){e.printStackTrace();
                }
            }
        }
    }

    /**
     * 小月月喝水
     */
    public void xiaoyueyue(){synchronized (obj){while (true){
                try{System.out.println("小月月喝水");
                    // 告诉他人喝水
                    obj.notify();
                    // 本人 wait
                    obj.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();
                    // 本人 await
                    cd.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();
                    // 本人 await
                    cd.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();
        // 可中断 进行 park
        Thread 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.

有问题能够留言哦,也能够公众号留言(回复快):

正文完
 0