引言
在AQS的实现中,当线程不能拿到锁时,通过调用LockSupport的park方法将线程挂起。这篇文章,我们来看LockSupport这个工具类的实现。提前说一下,LockSupport中的方法都是静态方法,所以它是工具类。
park方法
这个方法会将当前线程挂起。将当前线程挂起,意味着在使用LockSupport这个工具类的时候,线程的挂起只能由该线程自己执行,而不能由其他线程执行,也就是说,线程需要自己将自己挂起,哪个线程调用park方法,哪个线程就会挂起。
调用park方法后线程的状态
我们看下面的例子:
public class LockSupportTest {private void lockSupport() {LockSupport.park();}public static void main(String[] args) {LockSupportTest lockSupportTest = new LockSupportTest();Thread thread = new Thread(new Runnable() {@Overridepublic void run() {lockSupportTest.lockSupport();}}, "park_thread");thread.start();}}
使用jstack命令输出:
"park_thread" #11 prio=5 os_prio=31 tid=0x00007fb908039000 nid=0x5703 waiting on condition [0x0000700008021000]java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:304)at person.andy.concurrency.lock.LockSupportTest.lockSupport(LockSupportTest.java:7)at person.andy.concurrency.lock.LockSupportTest.access$000(LockSupportTest.java:5)at person.andy.concurrency.lock.LockSupportTest$1.run(LockSupportTest.java:14)at java.lang.Thread.run(Thread.java:748)
park响应中断
线程执行park方法处于WAITING状态后,能对中断作出响应。前面我们在讲Object.wait
方法时说过,线程调用wait方法后也会处于WAITING状态,也会响应中断,响应中断的方式是抛出InterruptedException异常。park方法的响应中断的方式与之不同,它并不会抛出InterruptedException异常,而是直接从park方法返回继续执行。
看下面的例子:
public class LockSupportTest {private void lockSupport() {LockSupport.park();System.out.println("线程继续执行");}public static void main(String[] args) throws InterruptedException {LockSupportTest lockSupportTest = new LockSupportTest();Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {lockSupportTest.lockSupport();}}, "park_thread");thread1.start();Thread.sleep(3000);System.out.println("开始中断线程");thread1.interrupt();}}
thread1调用park方法自行挂起之后主线程调用Thread.interrupt()方法对其进行了中断,thread1就会从park方法返回继续执行,也可以认为thread1被以中断的方式唤醒了。
输出如下:
开始中断线程线程继续执行
unpark方法
park方法将线程挂起之后,想要将其唤醒就需要调用unpark方法(前面说的中断也可以认为是唤醒的一种方式)。因为调用park的线程已经被挂起,所以unpark需要其他线程来调用,也就是说,在使用LockSupport类时,线程的挂起是自己挂起的,而线程的唤醒是由其他线程唤醒的。看下面的例子:
public class LockSupportTest {private void lockSupport() {LockSupport.park();System.out.println("线程继续执行");}public static void main(String[] args) throws InterruptedException {LockSupportTest lockSupportTest = new LockSupportTest();Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {lockSupportTest.lockSupport();}}, "park_thread");thread1.start();Thread.sleep(3000);System.out.println("开始唤醒线程");LockSupport.unpark(thread1);}}
开始唤醒线程线程继续执行
ParkNanos(long nanos)和ParkUntil(long deadline)方法
这两个方法对挂起做了时间限制,前者的参数代表线程要挂起的纳秒数,超出时间限制,线程就会自动唤醒。后者表示挂起当前线程直到deadline(1970年到deadline的毫秒数),过了这个时间,也会自动唤醒。
这两个方法就不再举例说明了。
带有object的方法
除了上面说的方法,LockSupport还对每个park方法提供了带有blocker的变体,就是下面这三个方法:
public static void park(Object blocker)public static void parkNanos(Object blocker, long nanos)public static void parkUntil(Object blocker, long deadline)
可以看到是对前面的每一个对应的方法增加了一个Object类型的参数,这个参数表示该线程挂起在哪个对象上,这个对象可以用来进行问题排查和系统监控。为了获取线程挂起的对象,LockSupport还提供了getBlocker方法,该方法的参数是被挂起的线程对象。
看下面的例子:
public class LockSupportTest {private void lockSupport() {LockSupport.park(this);}public static void main(String[] args) throws InterruptedException {LockSupportTest lockSupportTest = new LockSupportTest();Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {lockSupportTest.lockSupport();}}, "park_thread");thread1.start();Thread.sleep(3000);System.out.println(LockSupport.getBlocker(thread1));}}
thread1通过park(Object blocker)方法挂起在main方法中创建的LockSupportTest对象上,然后main线程通过调用LockSupport.getBlocker方法获得了thread1挂起的对象,输出如下:
person.andy.concurrency.lock.LockSupportTest@5e2de80c
我们通过jstack命令也能看到park方法挂起在哪个对象上:
"park_thread" #11 prio=5 os_prio=31 tid=0x00007fe99180a000 nid=0x5703 waiting on condition [0x0000700002e59000]java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for <0x000000076ac2c368> (a person.andy.concurrency.lock.LockSupportTest)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)at person.andy.concurrency.lock.LockSupportTest.lockSupport(LockSupportTest.java:8)at person.andy.concurrency.lock.LockSupportTest.access$000(LockSupportTest.java:5)at person.andy.concurrency.lock.LockSupportTest$1.run(LockSupportTest.java:15)at java.lang.Thread.run(Thread.java:748)
第四行说明了挂起在0x000000076ac2c368这个LockSupportTest对象上。
