JUC多并发编程 LockSupport和线程中断
中断机制
- 首先 一个线程不应该由其他线程来强制中断和停止,而是应该由线程自己自行停止。
- 其次 在 Java 中没有办法立即停止一条线程,然而停止线程线程却显得尤为重要,如取消一个耗时操作。因此,java 提供了一种用于停止线程的协商机制--中断,也即中断标识协商中断
- 中断只有一种协商机制,Java 没有给中断增加任何语法,中断的过程完全需要程序员自己实现。若要中断一个线程,你需要手动调用该线程的 interrupt 方法,该方法也仅仅是将线程对象的中断标识符设置成 true;
方法 | 说明 |
public void interrupt() |
实例方法 interrupt() 仅仅是设置线程的中断状态为true, 发起一个协商而不会立刻停止线程 |
public static boolean interrupted() |
静态方法 返回当前线程的中断状态,测试当前线程是否已被中断 将当前线程的中断状态清零并重新设为 false,清除线程的中断状态 |
public boolean isInterrupted() | 实例方法 判断当前线程是否呗中断(通过检查中断标志位) |
如何中断运行中的线程:
- 通过 一个 volatile 变量实现
- 通过 AtomicBoolean
- 通过 Thread 类 自带的 中断 API 实例方法实现
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;public class InterruptDemo {static volatile boolean isStop = false;static AtomicBoolean atomicBoolean = new AtomicBoolean(false);public static void main(String[] args) {Thread t1 = new Thread(() -> {while (true) {if (Thread.currentThread().isInterrupted()) {System.out.println(Thread.currentThread().getName() + "\\t isInterrupted 被修改为 true, 程序停止");break;}System.out.println("t1 ----- hello Interrupted");}}, "t1");t1.start();try{ TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(() -> {t1.interrupt();},"t2").start();}public static void m2() {new Thread(() -> {while(true) {if(atomicBoolean.get()) {System.out.println(Thread.currentThread().getName() + "\\t atomicBoolean 被修改为 true, 程序停止");break;}System.out.println("t1 ----- hello atomicBoolean");}},"t1").start();try{ TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(() -> {atomicBoolean.set(true);},"t2").start();}public static void m1() {new Thread(() -> {while(true) {if(isStop) {System.out.println(Thread.currentThread().getName() + "\\t isStop 被修改为 true, 程序停止");break;}System.out.println("t1 ----- hello volatile");}},"t1").start();try{ TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(() -> {isStop = true;},"t2").start();}
}
当前线程的中断标识为 true, 是不是线程就立刻停止:
- 如果线程处于 正常活动状态,那么会将该线程的中断标志设置为 true,被设置中断标志的线程正常运行,不受影响
- 如果线程处于被阻塞状态(sleep,wait,join 等状态),在别的线程中调用当前线程对象的interrupt 方法,那么线程将立即退出被阻塞状态,并抛出一个 InterruptedExecption 异常
import java.util.concurrent.TimeUnit;public class InterruptDemo2 {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) {// 如果不中断,则程序会进入死循环// Thread.currentThread().interrupt();e.printStackTrace();}System.out.println("hello InterruptDemo2");}}, "t1");t1.start();try{ TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(()-> t1.interrupt(),"t2").start();}
}
静态方法 Thread.interrupted() 理解:
public class InterrupteDemo4 {public static void main(String[] args) {System.out.println(Thread.currentThread().getName() + "\\t" + Thread.interrupted());System.out.println(Thread.currentThread().getName() + "\\t" + Thread.interrupted());System.out.println("-----1");Thread.currentThread().interrupt();System.out.println("-----2");System.out.println(Thread.currentThread().getName() + "\\t" + Thread.interrupted());System.out.println(Thread.currentThread().getName() + "\\t" + Thread.interrupted());}}public static boolean interrupted() {return currentThread().isInterrupted(true);}public boolean isInterrupted() {return isInterrupted(false);}private native boolean isInterrupted(boolean ClearInterrupted);
LockSupport
- 用来创建锁和其他同步类的基本线程阻塞原语
- park() 和 unpark() 的作用分别是阻塞线程和解除线程阻塞(LockSupport 和 每个使用它的线程都是一个许可关联,每个线程都有一个相关的 permit,permit 最多只有一个,重复调用 unpart 也不会积累凭证)
- 是一个线程阻塞工具类,所有发的方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法,归根揭底,LockSupport 调用的 Unsafe 中的 native 代码。
- park 方法: 如果有凭证,则会直接消耗掉这个凭证然后正常退出,如果无凭证,就必须阻塞等待凭证可用
- unpark方法: 会增加一个凭证,单凭证只能有一个,累加无效
线程等待唤醒机制
- 使用 Object 中的wait() 方法让线程等待,使用 Object 中的 notify() 方法唤醒线程
import java.util.concurrent.TimeUnit;public class LockSupportDemo {public static void main(String[] args) {Object objectLock = new Object();// 必须放在同步块或者方法里// 将 notify 放在 wait 方法前面,程序无法执行,无法唤醒new Thread(()-> {synchronized (objectLock) {System.out.println(Thread.currentThread().getName() + "\\t ----come in");try {objectLock.wait();}catch (InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "\\t ----被唤醒");}},"t1").start();try{ TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(()-> {synchronized (objectLock) {objectLock.notify();System.out.println(Thread.currentThread().getName() + "\\t ----发出通知");}},"t2").start();}
}
- 使用 JUC 报中的 Condition的 await() 方法让线程等待,使用 signal() 方法唤醒线程
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockSupportDemo {public static void main(String[] args) {Lock lock = new ReentrantLock();Condition condition = lock.newCondition();// 必须放在同步块或者方法里// 将 signal 放在 await 方法前面,程序无法执行,无法唤醒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();}},"t1").start();try{ TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(()-> {lock.lock();condition.signal();System.out.println(Thread.currentThread().getName() + "\\t ----发出通知");lock.unlock();},"t2").start();}
}
- LockSupport 类 可以阻塞当前线程以及 唤醒执行被阻塞线程
public class LockSupportDemo {/* Permit 许可证默认没有不能放行,所以一开始调 park() 方法当前线程就会阻塞,直到别的线程给当前线程的发放 permit, park 方法还会被唤醒* 调用 uppark(thread) 方法后,就会将 thread 线程的许可证 permit 发放,会自动唤醒 park 线程,即之前阻塞中的 LockSupport.park() 方法会立即返回* @param args*/public static void main(String[] args) {// 无锁块要求// 加锁和解锁不需要顺序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();}
}