一.线程中断标记
1.interrupt
实例方法interrupt()仅仅是设置线程的中断状态为true,不会停止线程
1.如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true。
被设置中断标志的线程将继续正常运行,不受影响。所以,interrupt()并不能真正的中断线程,需要被调用的线程自己进行配合才行。
2.如果线程处于被阻塞状态(例如处于sleep, wait,join等状态),在别的线程中调用当前线程对象的interrupt方法,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。并且会将中断状态置为false,需要重新将标志为置为true
public static void m5()
{
Thread t1 = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("-----isInterrupted() = true,程序结束。");
break;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
System.out.println("------hello Interrupt");
}
}, "t1");
t1.start();
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(() -> {
t1.interrupt();
},"t2").start();
}
2.interrupted
判断线程是否被中断,并清除当前中断状态这个方法做了两件事:
1返回当前线程的中断状态
2将当前线程的中断状态设为false
3.isInterrupted
判断当前线程是否被中断(通过检查中断标志位)
二.LockSupport
1.1 LockSupport.unpark();
会给指定线程发通行证,一个线程只能发一次,如果要发多个需要开多个线程去unpark();
1.2LockSupport.park();
线程加锁,可以锁多次,锁几次就得解几次,但是不能提前解多次,只能解一次
提前发了两次通行证,但只有一个生效,线程无法终止
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
Thread.sleep(2000);
System.out.println("t1开始运行---");
} catch (InterruptedException e) {
e.printStackTrace();
}
LockSupport.park();
LockSupport.park();
});
thread.start();
new Thread(() ->{
System.out.println("开始解锁11111");
LockSupport.unpark(thread);
}).start();
new Thread(() ->{
System.out.println("开始解锁22222");
LockSupport.unpark(thread);
}).start();
}
先上锁多次再多次解锁可以成功,但是如果出现同时解锁会导致线程一直运行
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
System.out.println("t1开始运行---");
} catch (Exception e) {
e.printStackTrace();
}
LockSupport.park();
LockSupport.park();
});
thread.start();
new Thread(() ->{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开始解锁11111");
LockSupport.unpark(thread);
}).start();
new Thread(() ->{
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开始解锁22222");
LockSupport.unpark(thread);
}).start();
}
三、JMM
1、JMM定义
Java虚拟机规范中试图定义一种Java内存模型《java Memory Model,简称JMM)来屏蔽掉各种硬件和操作系统的内存访问差异以实现让Java程序在各种平台下都能达到一致的内存访问效果。
JMM(Java内存模型Java Memory Model,简称JMM)本身是一种抽象的概念1不真实存在它仅仅描述的是一组约定或规范,通过这组规范定义了程序中(尤其是多线程)各个变量的读写访问方式并决定一个线程对共享变量的写入何时以及如何变成对另一个线程可见,关键技术点都是围绕多线程的原子性、可见性和有序性展开的。
作用:
1通过JMM来实现线程和主内存之间的抽象关系。
2屏蔽各个硬件平台和操作系统的内存访问差异以实现让Java程序在各种平台下都能达到一致的内存访问效果。
2.JMM三大特性
2.1 可见性
是指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道该变更,JMM规定了所有的变量都存储在主内存中。
2.2有序性
对于一个线程的执行代码而言,我们总是习惯性认为代码的执行总是从上到下,有序执行。但为了提供性能,编译器和处理器通常会对指令序列进行重新排序。
指令重排可以保证串行语义一致,但没有义务保证多线程间的语义也一致,即可能产生"脏读",简单说,
两行以上还相干的代码在执行的时候有可能先执行的不是第一条,不见得是从上到下顺序执行,执行顺序会被优化。
单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致。处理器在进行重排序时必须要考虑指令之间的数据依赖性
多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测
2.3
指一个操作是不可中断的,即多线程环境下,操作不能被其他线程干扰
3.JVM读取过程
由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),
工作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,
所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,
首先要将变量从主内存拷贝到的线程自己的工作内存空间,然后对变量进行操作,
操作完成后再将变量写回主内存,不能直接操作主内存中的变量,各个线程中的工作内存中存储着主内存中的变量副本拷贝,
因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成。:
线程和主内存之间的关系
1线程之间的共享变量存储在主内存中(从硬件角度来说就是内存条)
2每个线程都有一个私有的本地工作内存,本地工作内存中存储了该线程用来读I写共享变量的副本(从硬件角度来说就是CPU的缓存,比如寄存器、L1、L2、L3缓存等)
|