java线程挂起唤醒_JAVA并发(10)—interrupt唤醒挂起线程

1.1 中断方法

在独占锁加锁过程中,我们看到,线程进入sync queue中后便调用park()方法将自己挂起。等待其他线程调用unpark()方法唤醒自己。那么当我们调用interrupt()方法时,是否可以中断被操作系统挂起的线程呢?

public class ParkDemo {

public static void main(String[] args) {

Thread t1 = new Thread(()->{

System.out.println("park 开始");

LockSupport.park();

System.out.println("park 结束");

});

//线程挂起

t1.start();

//中断t1

t1.interrupt();

}

}

实际上,park()的线程实际上会被“唤醒”。

park 开始

park 结束

总结:无论是interrupt()方法还是unpark()方法,均是将线程唤醒。线程依赖其他标识位来判断它是被正常唤醒还是中断唤醒的。

1.2 lock方法中调用中断方法

下列代码的流程:

t1线程先获取锁,并且持有2s的时间;

t2线程未获取锁,会调用park()方法自我阻塞;

主线程调用t2线程的interrupted()方法中断线程;

@Slf4j

public class Demo {

public static void main(String[] args) throws InterruptedException {

ReentrantLock reentrantLock = new ReentrantLock();

/**

* 开启线程,并获取锁,线程1持有锁

*/

Thread t1 = new Thread(() -> {

reentrantLock.lock();

log.info("t1:打印数据");

try {

Thread.sleep(2000);

} catch (InterruptedException e) {

e.printStackTrace();

}

reentrantLock.unlock();

});

t1.start();

Thread.sleep(100);

Thread t2 = new Thread(() -> {

//线程加入到sync queue中,即线程被阻塞

reentrantLock.lock();

log.info("t2:打印数据");

if(Thread.interrupted()){

log.warn("t2:线程被中断过");

}

reentrantLock.unlock();

});

//t2开启线程

t2.start();

Thread.sleep(100);

t2.interrupt();

log.info("主线程");

}

}

执行结果:

13:05:35.882 [Thread-0] INFO com.tellme.lock.conditionLock.Demo - t1:打印数据

13:05:36.071 [main] INFO com.tellme.lock.conditionLock.Demo - 主线程

13:05:37.933 [Thread-1] INFO com.tellme.lock.conditionLock.Demo - t2:打印数据

13:05:37.934 [Thread-1] WARN com.tellme.lock.conditionLock.Demo - t2:线程被中断过

t2线程被唤醒(中断)之后,它会再次争夺独占锁,若争夺失败,会再次被阻塞。

final boolean acquireQueued(final Node node, int arg) {

boolean failed = true;

try {

boolean interrupted = false;

for (;;) {

//自旋再次获取锁,

final Node p = node.predecessor();

if (p == head && tryAcquire(arg)) {

setHead(node);

p.next = null; // help GC

failed = false;

//注意,获取到锁后,最终结果返回的是`中断标识`!

return interrupted;

}

if (shouldParkAfterFailedAcquire(p, node) &&

parkAndCheckInterrupt()) //我们在这,线程被唤醒

//中断标识修改为true。

interrupted = true;

}

} finally {

if (failed)

cancelAcquire(node);

}

}

public final void acquire(int arg) {

if (!tryAcquire(arg) &&

acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

//若是最终返回true,则修改中断标识。

/**

** static void selfInterrupt() {

** Thread.currentThread().interrupt();

** }

**/

selfInterrupt();

}

结论:若调用lock方法进行加锁,若线程被阻塞后,调用interrupt()中断线程,线程会立即被唤醒,但是唤醒之后会再去争夺锁,若获取锁失败,该线程依旧被阻塞。用户可以在方法内部通过使用if(Thread.interrupted())判断线程是否被中断过。

1.3 lockInterruptibly方法

@Slf4j

public class Demo {

public static void main(String[] args) throws InterruptedException {

ReentrantLock reentrantLock = new ReentrantLock();

/**

* 开启线程,并获取锁,线程1持有锁

*/

Thread t1 = new Thread(() -> {

reentrantLock.lock();

log.info("t1:打印数据");

try {

Thread.sleep(2000);

} catch (InterruptedException e) {

e.printStackTrace();

}

reentrantLock.unlock();

});

t1.start();

Thread.sleep(100);

Thread t2 = new Thread(() -> {

//线程加入到sync queue中,即线程被阻塞

try {

reentrantLock.lockInterruptibly();

} catch (InterruptedException e) {

e.printStackTrace();

}

log.info("t2:打印数据");

if(Thread.interrupted()){

log.warn("t2:线程被中断过");

}

reentrantLock.unlock();

});

//t2开启线程

t2.start();

Thread.sleep(100);

t2.interrupt();

log.info("主线程");

}

}

执行结果:

13:16:58.585 [Thread-0] INFO com.tellme.lock.conditionLock.Demo - t1:打印数据

13:16:58.782 [main] INFO com.tellme.lock.conditionLock.Demo - 主线程

java.lang.InterruptedException

at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)

at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)

at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)

at com.tellme.lock.conditionLock.Demo.lambda$main$1(Demo.java:35)

at java.lang.Thread.run(Thread.java:748)

13:16:58.786 [Thread-1] INFO com.tellme.lock.conditionLock.Demo - t2:打印数据

我们可以看到,t2线程调用park()方法,被操作系统挂起。之后主线程调用t2.interrupt();后,t2线程抛出异常,真正中断线程。

private void doAcquireInterruptibly(int arg)

throws InterruptedException {

final Node node = addWaiter(Node.EXCLUSIVE);

boolean failed = true;

try {

for (;;) {

final Node p = node.predecessor();

if (p == head && tryAcquire(arg)) {

setHead(node);

p.next = null; // help GC

failed = false;

return;

}

if (shouldParkAfterFailedAcquire(p, node) &&

parkAndCheckInterrupt()) //我们在这被唤醒

//唤醒之后,直接抛出异常。

throw new InterruptedException();

}

} finally {

if (failed)

cancelAcquire(node);

}

}

相关阅读

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页