引言
在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() {
@Override
public 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() {
@Override
public 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() {
@Override
public 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() {
@Override
public 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对象上。