1. interrupt() 相关方法
interrupt(),interrupted()
和 isinterrupted()
的区别
public void interrupt():
将线程的中断标记设置为 true,并不是真的停止该线程。
如果线程处于 wait()、join()、sleep() 方法的阻塞当中。中断标记会被清除。也就是通过isInterrupted,interrupted 方法返回均为 false。同时抛出一个InterruptedException异常。
public boolean isInterrupted()
判断某个具体线程对象是否被中断,返回的是线程的中断标记(true or false)
public static boolean interrupted()
返回当前线程的中断状态,然后将当前线程的中断状态设为false。
如果线程中断标记为true,只有第一次调用时会返回 true。之后调用都是false。(这个方法会清除线程的中断状态)
- 三者的使用
public static void interruptMethod03() throws InterruptedException {
Thread t1 = new Thread(()->{
// 当要 interrupt 一个正常运行线程时,
// 线程的打断标记会被赋值为 true ,但是线程不会立刻中断。
Thread now = Thread.currentThread();
int i = 0;
boolean flag = false;
while(true){
flag = Thread.interrupted();
System.out.println(++i);
if(flag){
// 可以说明:Thread.interrupted() 是清除打断标记
System.out.println("t1.interrupt()执行后,第一次调用 Thread.interrupted(). 应该返回true");
System.out.println("flag: "+ flag);
System.out.println("t1.interrupt()执行后,第二次调用 Thread.interrupted(). 应该返回false");
System.out.println("Thread.interrupted(): "+Thread.interrupted());
System.out.println();
System.out.println("再一次调用 t1.interrupt() 之后");
now.interrupt();
System.out.println("now.isInterrupted()【不会将打断状态设置为false】. 应该返回true");
System.out.println("now.isInterrupted(): "+now.isInterrupted());
System.out.println("Thread.interrupted()【将打断状态设置为false】.当前应该返回true");
System.out.println("Thread.interrupted(): "+Thread.interrupted());
System.out.println("经过Thread.interrupted()对打断状态的清除,now.isInterrupted()应该返回false,now.isInterrupted(): "
+now.isInterrupted());
// 当前线程已经被打断(也即其他线程调用了当前线程的interrupt方法)
System.out.println(now.getName() + " 线程被打断.....");
break;
}
}
},"t1");
t1.start();
Thread.sleep(100);
t1.interrupt();
}
通过上面的例子可以了解到:
在Java中没有办法立即停止一条线程(处于阻塞状态除外),然而停止线程却显得尤为重要,如取消一个耗时操作。
因此,Java提供了一种用于停止线程的协商机制——中断。
中断只是一种协作协商机制,Java没有给中断增加任何语法,中断的过程完全需要程序员自己实现。
比如线程A调用了线程B的
public void interrupt():
方法。线程B只能在 run 方法中使用isInterrupted
与interrupted
方法的返回值确定是否中断,然后自行编写中断逻辑。
- 阻塞线程被中断的情况
public static void main(String[] args) {
Thread t1 = new Thread(()->{
while(true){
if(Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+"\t"+
"中断标志位:"+Thread.currentThread().isInterrupted()+"程序终止");
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
// 加了这个,程序可以终止,只会爆异常
// 如若不在异常处理部分加入 interrupt,程序会一直报异常。
// 因为 处于阻塞的线程被 interrupt 后,会将线程中断标记设置为false。
// 这样就导致 Thread.currentThread().isInterrupted() 返回false,程序无法中断
Thread.currentThread().interrupt();
}
System.out.println("-----hello InterruptDemo03");
}
},"t1");
t1.start();
try {
TimeUnit.MILLISECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
new Thread(() -> t1.interrupt()).start();
}
2. LockSupport
LockSupport
是用来创建锁
和其他同步类
的基本线程阻塞原语
,其中park()
和unpack()
而作用分别是阻塞线程
和解除阻塞线程
。
3. 线程的等待唤醒机制
- 方式一:使用
Object
中的wait()
方法让线程等待,使用Object
中的notify()
方法唤醒线程 - 方式二:使用
JUC
包中的Condition
的await()
方法让线程等待,使用signal()
方法唤醒线程 - 方式三:
LockSupport
类可以阻塞当前线程
以及唤醒指定被阻塞的线程
3.1 Object中wait和notify
wait
与notify
需要成对出现,且需要在同步代码块或同步方法中使用。- 要先
wait
,后notify
。
public static void main(String[] args) {
Object objectLock = new Object();
new Thread(()->{
synchronized(objectLock){
System.out.println(Thread.currentThread().getName()+" come in 。。。");
try {
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 被唤醒。。。");
}
},"Thread 01").start();
new Thread(()->{
synchronized(objectLock){
System.out.println(Thread.currentThread().getName()+" come in 。。。");
try {
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 被唤醒。。。");
}
},"Thread 03").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
synchronized(objectLock){
// 如果只有两个线程,一个等待,一个唤醒。就调用notify
// objectLock.notify();
// 如果有多个线程等待,就需要调用 notifyAll。 一次唤醒全部等待线程
objectLock.notifyAll();
System.out.println(Thread.currentThread().getName()+" 唤醒通知已经发送。。。");
}
},"Thread 02").start();
}
3.2 Condition中await和signal
- Condition中的线程等待和唤醒方法,需要先获取锁
- 一定要先await后signal,不要反了
Condition
可以拥有多个,这样面对不同的场景,可以创建不同的condition
。解决Object
对象中多个wait
只能notifyAll
的情况。
// 方式二:使用`JUC`包中的`Condition`的`await()`方法让线程等待,使用`signal()`方法唤醒线程
public static void awaitAndSignal(){
Lock lock = new ReentrantLock();
// 可以针对不同的业务拥有不同的 condition,等待唤醒可以达到具体某个线程的粒度。
Condition condition = lock.newCondition();
Condition conditionThread03 = lock.newCondition();
new Thread(()->{
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\t -----------come in");
condition.await();
System.out.println(Thread.currentThread().getName() + "\t -----------被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
},"Thread 01").start();
new Thread(()->{
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\t -----------come in");
conditionThread03.await();
System.out.println(Thread.currentThread().getName() + "\t -----------被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
},"Thread 03").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
lock.lock();
try {
condition.signal();
conditionThread03.signal();
System.out.println(Thread.currentThread().getName() + "\t -----------发出通知");
} finally {
lock.unlock();
}
},"Thread 02").start();
}
3.3 Object和Condition使用的限制条件
- wait 与 notify 必须在 synchronized 快中使用,否则会报异常。
- await 与 signal 需要配合 lock 使用。
- 二者的等待与唤醒的顺序是不能错的。
3.4 LockSupport类中的park等待和unpark唤醒
LockSupport
使用了一种名为Permit
(许可)的概念来做到阻塞和唤醒线程的功能- 每个线程都有一个许可(
permit
),且累加上限为1(也就是说,要么有一个许可,要么没有许可)。 - 当程序调用
LockSupport.park();
时,如果当前线程没有许可,就进行阻塞,有许可就放行。 LockSupport.unpark(t1);
给t1
线程一个许可(permit),
例子:
// 方式三:`LockSupport`类可以`阻塞当前线程`以及`唤醒指定被阻塞的线程`
// LockSupport 使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能.每个线程都有一个许可(Permit),许可证只能有一个,累加上限是1。
public static void parkAndUnpark(){
/**
* t1 -----------come in
* t2 ----------发出通知
* t1 ----------被唤醒
*/
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t -----------come in");
LockSupport.park();
System.out.println(Thread.currentThread().getName() + "\t ----------被唤醒");
}, "t1");
t1.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
LockSupport.unpark(t1);
System.out.println(Thread.currentThread().getName() + "\t ----------发出通知");
}, "t2").start();
}
例子:先调用 unpark , 在调用 park。不会报异常,也不会阻塞。
/*
先调用 unpark 后调用 park
*/
public static void parkAndUnpark02(){
/**
* t1 -----------come in
* t2 ----------发出通知
* t1 ----------被唤醒
*/
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t -----------come in");
System.out.println(Thread.currentThread().getName() + "\t ----------unpark");
LockSupport.unpark(Thread.currentThread());
System.out.println(Thread.currentThread().getName() + "\t ----------park");
// 上面已经 unpark ,得到了一个 permit。
// 所以调用一次 park 不会产生阻塞。
// 但是如果调用2两次及以上,就会产生阻塞。应该 permit 不累加,最大为1。
LockSupport.park();
System.out.println(Thread.currentThread().getName() + "不会阻塞");
}, "t1");
t1.start();
}
- 其他方法
权限 | 方法签名与解释 | |
---|---|---|
static Object | getBlocker(Thread t) | Returns the blocker object supplied to the most recent invocation of a park method that has not yet unblocked, or null if not blocked. |
static void | park() | Disables the current thread for thread scheduling purposes unless the permit is available. |
static void | park(Object blocker) | Disables the current thread for thread scheduling purposes unless the permit is available. |
static void | parkNanos(long nanos) | Disables the current thread for thread scheduling purposes, for up to the specified waiting time, unless the permit is available. |
static void | parkNanos(Object blocker, long nanos) | Disables the current thread for thread scheduling purposes, for up to the specified waiting time, unless the permit is available. |
static void | parkUntil(long deadline) | Disables the current thread for thread scheduling purposes, until the specified deadline, unless the permit is available. |
static void | parkUntil(Object blocker, long deadline) | Disables the current thread for thread scheduling purposes, until the specified deadline, unless the permit is available. |
static void | unpark(Thread thread) | Makes available the permit for the given thread, if it was not already available. |