1.简介
LockSupport 类与每一个使用他的线程都会去关联一个许可证,在默认的情况下调用LockSupport类的方法的线程是不持有许可证的。LockSupport是使用UnSafe类实现的
2.lockSupport的主要方法介绍
2.1 void park()方法
如果调用的线程已经拿到了与LockSupport相关的许可证,则调用LockSupport.park()方法会立即返回,否则会被禁止参与线程的调度,也就是会被阻塞挂起
示例代码
public static void main(String[] args) {System.out.println("Lock Support begin park");LockSupport.park();System.out.println("Lock Support end park");LockSupport.unpark(Thread.currentThread());}
执行结果
注意点:在其他线程调用 unpark(Thread t)将当前线程作为参数时,调用park()方法而被阻塞的线程会返回,另外如果其他线线程调用了阻塞线程的 interrupt()方法时,设置了中断标记或者线程被虚假唤醒,则阻塞线程也会返回,所以在调用park方法时最好使用循环条件
示例代码**
static ExecutorService threadPoolExecutor = new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(5),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());public static void main(String[] args) {Thread main = Thread.currentThread();threadPoolExecutor.submit(() -> {try {Thread.sleep(2000);//LockSupport.unpark(main);main.interrupt();} catch (InterruptedException e) {e.printStackTrace();}});System.out.println("Lock Support begin park");LockSupport.park();System.out.println("Lock Support end park");threadPoolExecutor.shutdown();}
可以看到 LockSupport.unpark()方法和 interrupt()方法都会中断park()方法,且不会抛出任何异常
2.2 void unpark(Thread t)方法
当一个线程调用 unpark() 方法的时候,如果参数 thread 线程没有持有 thread 和 LockSupport类关联的许可证,则让 thread线程持有。如果thread线程之前因为调用park()方法而被挂起,则调用 unpark() 方法后,该线程会被唤醒,如果thread 之前没有调用 park() 方法,则调用 unpark() 后再次调用 park()会立即返回
示例代码
**
public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {System.out.println("child Thread begin park");while (!Thread.currentThread().isInterrupted()) {LockSupport.park();}System.out.println("child Thread end park");}, "a");Utils.getThreadPool().submit(t1);System.out.println("main call child Thread");Thread.sleep(1000);//LockSupport.unpark(t1); //子线程并不能被唤醒,因为外面嵌套循环条件//这里关闭线程就是中断了线程池中正在运行的线程,会输出 “child Thread end park”Utils.getThreadPool().shutdownNow();}
执行结果:
2.3 void parkNanos(long nanos) 方法
和 park() 方法相似,拿到许可证后会立即返回,拿不到许可证则会等待,挂起 nanos 时间后会修改为自动返回
2.4 void park(Object blocker) 方法
park()方法还可以支持带有参数的方法,当线程内部没有许可证的情况下而被挂起,则这个blocker对象会被记录到该线程的内部
作用:
我们可以使用诊断工具来观察线程阻塞的原因,诊断工具是通过调用 getBlocker()方法获取blocker对象的
所以JDK推荐我们使用带有blocker的参数的park()方法,并且blocker设置为this,就可以知道那个线程被阻塞了
**
示例代码:
public class LockSupportTest03 {public void testPark(){LockSupport.park(this);}public static void main(String[] args){LockSupportTest03 test = new LockSupportTest03();test.testPark();}}
我们可以使用 jps 和 jstack 两个命令查询对应的信息
如下:
F:\code\test>jps20228 Jps20292 Launcher20376 LockSupportTest03main" #1 prio=5 os_prio=0 tid=0x0000021f87a8a800 nid=0x2ea8 waiting on condition [0x000000b193aff000]java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for <0x00000007822c6d38> (a cn.jn.lhm.part01.LockSupportTest03)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)at cn.jn.lhm.part01.LockSupportTest03.testPark(LockSupportTest03.java:12)at cn.jn.lhm.part01.LockSupportTest03.main(LockSupportTest03.java:17)
可能出现的问题:
jps命令不可用原因
源码解读:
public static void park(Object blocker) {//获取当前的线程Thread t = Thread.currentThread();//设置该线程的blocker变量setBlocker(t, blocker);//挂起线程UNSAFE.park(false, 0L);//线程激活后清楚blockersetBlocker(t, null);}
2.5 void parkUtil(Object blocker, long deadline) 方法
源码:
public static void parkUntil(Object blocker, long deadline) {Thread t = Thread.currentThread();setBlocker(t, blocker);//这里的 deadline 是指的是从1970开始计算的时间数 msUNSAFE.park(true, deadline);setBlocker(t, null);}
3.实战LockSupport
设计一个线程先进先出的锁
示例代码
**
public class FIFOMutex {private AtomicBoolean locked = new AtomicBoolean(false);private Queue<Thread> queue = new ConcurrentLinkedQueue<>();public void lock(){boolean isInterrupted = false;Thread thread = Thread.currentThread();queue.add(thread);while (queue.peek() != thread || !locked.compareAndSet(false, true)){LockSupport.park(this);if (thread.isInterrupted()){isInterrupted = true;}}queue.remove(thread);if (isInterrupted){thread.interrupt();}}public void unlock(){//cas 恢复成没有获取锁的样子locked.set(false);//释放队列头部的线程获取的锁LockSupport.unpark(queue.peek());}}
