关于线成暂停和恢复的那些事儿
2020-04-18 本文已影响0人
allen丿
关于线成暂停和恢复的那些事儿
suspend()和resume()
这是一对相反的操作,suspend可以挂起当前线成,而resume可以恢复当前线成。 乍一看这对操作非常有用,但是如果对jdk源码稍微有点了解,会发现这两个方法早就废弃了。
为什么会废弃呢?
因为如果resume操作,出现在suspend之前的话,线成会永久阻塞,会影响到后面访问当前临界区的所有线成。最误导人的是,当前线成的状态是runnable。
suspend问题演示
package com.allen.dayup.高并发程序设计.chap1;
/**
* @Auther: allen
* @Date: 2020-03-19 21:16
* @Description:
*/
public class BadSuspend {
static Object u = new Object();
static ChangeObjectThread t1 = new ChangeObjectThread("t1");
static ChangeObjectThread t2 = new ChangeObjectThread("t2");
public static class ChangeObjectThread extends Thread{
public ChangeObjectThread(String name) {
super.setName(name);
}
@Override
public void run() {
synchronized (u){
System.out.println("in " + Thread.currentThread().getName());
Thread.currentThread().suspend();
}
}
}
public static void main(String[] args) throws InterruptedException{
//对照观察t1和t2,
//休眠主线程让t1阻塞:t1线成是先调用suspend,然后再resume
//主线程启动t2后直接调用resume:t2线程的resume会在suspend之前调用
t1.start();
//主线程休眠100毫秒,保证t1已经阻塞
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
t1.resume();
t2.resume();
//等待t1和t2执行完成后,主线程再继续执行
t1.join();
t2.join();
}
}
运行程序会发现主线程会一直阻塞,用jstack查看线成信息发现t2线成是runnable状态
C:\Users\allen>jstack 20336
2020-04-18 09:11:31
Full thread dump OpenJDK 64-Bit Server VM (25.161-b14 mixed mode):
"t2" #15 prio=5 os_prio=0 tid=0x000000001f4a60f0 nid=0x42f0 runnable [0x00000000203af000]
java.lang.Thread.State: RUNNABLE
at java.lang.Thread.suspend0(Native Method)
at java.lang.Thread.suspend(Thread.java:1032)
at com.allen.dayup.高并发程序设计.chap1.BadSuspend$ChangeObjectThread.run(BadSuspend.java:26)
- locked <0x000000076b8d2618> (a java.lang.Object)
"Service Thread" #13 daemon prio=9 os_prio=0 tid=0x000000001f387510 nid=0x56c0 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #12 daemon prio=9 os_prio=2 tid=0x000000001f2efab0 nid=0x34a8 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #11 daemon prio=9 os_prio=2 tid=0x000000001f2ef630 nid=0x256c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #10 daemon prio=9 os_prio=2 tid=0x000000001f2ef1b0 nid=0x4c90 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #9 daemon prio=9 os_prio=2 tid=0x00000000035164f0 nid=0x5530 waiting on condition [0x0000000000000000]
利用wait和notify,用一个钩子方法来阻塞和恢复线成
public class GoodSuspend {
static Object lock = new Object();
static class ChangeObjectThread extends Thread {
volatile boolean suspendMe = false;
public void suspendMe(){
this.suspendMe = true;
}
public void resumeMe(){
suspendMe = false;
synchronized (this){
notify();
}
}
@Override
public void run() {
while (true){
//如果suspendMe为true,调用wait方法阻塞当前线成;否则,就执行业务逻辑
synchronized (this) {
while (suspendMe) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (lock) {
System.out.println("in ChangeObjectThread!");
}
Thread.yield();
}
}
}
}
static class ReadObjectThread extends Thread{
@Override
public void run() {
while (true){
synchronized (lock){
System.out.println("in ReadObjectLock!");
Thread.yield();
}
}
}
}
public static void main(String[] args) throws InterruptedException{
ChangeObjectThread t1 = new ChangeObjectThread();
ReadObjectThread t2 = new ReadObjectThread();
t1.start();
t2.start();
Thread.sleep(1000);
t1.suspendMe();
System.out.println("=====================》suspend t1 2 second");
Thread.sleep(2000);
System.out.println("resume t1");
t1.resumeMe();
}
}
- 启动t1和t2线成,t1和t2交替输出一秒。
- 然后阻塞t1两秒,就只有t2线成输出
- 最后恢复t1,又重新交替输出
park()和unpark()
不过对于线成暂停和恢复,再jdk的并发包中提供了LockSupport的处理。
- park():阻塞线程
- unpark():恢复线程
在park实现中,阻塞线成之前先去请求一个许可,请求到才能阻塞线成。只有线成unpark后,许可才会变成可用状态。
所以就算unpark发生在park之前,也不会导致线成永久挂起。
public class LockSupportDemo {
static Object u = new Object();
static ChangeObjectThread t1 = new ChangeObjectThread("t1");
static ChangeObjectThread t2 = new ChangeObjectThread("t2");
static class ChangeObjectThread extends Thread {
public ChangeObjectThread(String name) {
super(name);
}
@Override
public void run() {
synchronized (u){
System.out.println("in " + getName());
LockSupport.park();
}
}
}
public static void main(String[] args) throws InterruptedException {
t1.start();
Thread.sleep(100);
t2.start();
LockSupport.unpark(t1);
LockSupport.unpark(t2);
t1.join();
t2.join();
}
}
这段代码只是将suspend和resume,替换成了park和unpark。执行结果会发现线成正常运行结束。
参考文档
实战java高并发程序 葛一鸣