网站设计与建设考试,如何在网络上推广产品,北京企业网站建站哪家好,自己能制作免费网站吗目录
1.概念
1.1.单线程
1.2.多线程
2.导致线程不安全的5个因素
①抢占式执行#xff08;首要原因#xff09;
②多个线程同时修改了同一个变量
③非原子性操作
④内存可见性
⑤指令重排序 线程优点#xff1a;加速程序性能。线程缺点#xff1a;存在安全问题。
1…目录
1.概念
1.1.单线程
1.2.多线程
2.导致线程不安全的5个因素
①抢占式执行首要原因
②多个线程同时修改了同一个变量
③非原子性操作
④内存可见性
⑤指令重排序 线程优点加速程序性能。线程缺点存在安全问题。
1.概念
线程不安全指的是程序在多线程的执行结果不符合预期。 例如
1.1.单线程
public class ThreadDemo17 {static class Counter{//变量private int number 0;//循环次数private int MAX_COUNT 0;public Counter(int MAX_COUNT) {this.MAX_COUNT MAX_COUNT;}//方法public void incr() {for (int i 0; i MAX_COUNT; i) {number;}}//--方法public void decr() {for (int i 0; i MAX_COUNT; i) {number--;}}public int getNumber() {return number;}}public static void main(String[] args) {Counter counter new Counter(100000);//操作counter.incr();//--操作counter.decr();//打印结果System.out.println(最终结果 counter.getNumber());}
}1.2.多线程
public class ThreadDemo17 {static class Counter{//变量private int number 0;//循环次数private int MAX_COUNT 0;public Counter(int MAX_COUNT) {this.MAX_COUNT MAX_COUNT;}//方法public void incr() {for (int i 0; i MAX_COUNT; i) {number;}}//--方法public void decr() {for (int i 0; i MAX_COUNT; i) {number--;}}public int getNumber() {return number;}}public static void main(String[] args) throws InterruptedException {Counter counter new Counter(100000);Thread t1 new Thread(() - {//操作counter.incr();});Thread t2 new Thread(() - {//--操作counter.decr();});//启动多线程进行执行t1.start();t2.start();//等待两个线程执行完t1.join();t2.join();//打印结果System.out.println(最终结果 counter.getNumber());}
}每次执行结果都不同。这就是线程不安全。
2.导致线程不安全的5个因素
①抢占式执行首要原因
由于CPU资源较少而线程较多狼多肉少发生争抢混乱。
若将上面代码改为串行执行线程1执行完之后线程2再执行相当于单线程效率就不会争抢。
public static void main(String[] args) throws InterruptedException {Counter counter new Counter(100000);Thread t1 new Thread(() - {//操作counter.incr();});t1.start();t1.join();Thread t2 new Thread(() - {//--操作counter.decr();});t2.start();t2.join();System.out.println(最终结果 counter.getNumber());} ②多个线程同时修改了同一个变量
改动代码让不同线程各自修改各自的变量就ok了。
public class ThreadDemo17 {static class Counter{//变量private int number 0;//循环次数private int MAX_COUNT 0;public Counter(int MAX_COUNT) {this.MAX_COUNT MAX_COUNT;}//方法public int incr() {int temp 0;for (int i 0; i MAX_COUNT; i) {temp;}return temp;}//--方法public int decr() {int temp 0;for (int i 0; i MAX_COUNT; i) {temp--;}return temp;}public int getNumber() {return number;}}static int num1 0;static int num2 0;public static void main(String[] args) throws InterruptedException {Counter counter new Counter(100000);Thread t1 new Thread(() - {//操作num1 counter.incr();});Thread t2 new Thread(() - {//--操作num2 counter.decr();});t1.start();t2.start();t1.join();t2.join();System.out.println(最终结果 (num1 num2));}
}③非原子性操作 什么是原子性 ——将一组操作封装成一个执行单元要一次性执行完中间不能停顿。 一条 java 语句不一定是原子的也不一定只是一条指令。 ⽐如刚才的 n其实是由三步操作组成的 从内存把数据读到 CPU 进⾏数据更新 把数据写回到 CPU不保证原子性会给多线程带来什么问题 如果⼀个线程正在对⼀个变量操作中途其他线程插⼊进来了如果这个操作被打断了结果就可能是错误的。 这点也和线程的抢占式调度密切相关. 如果线程不是 抢占 的, 就算没有原⼦性, 也问题不⼤。 例如 把⼀段代码想象成⼀个房间每个线程就是要进⼊这个房间的⼈。如果没有任何机制保证A进⼊房间后还没出来B 也进⼊房间打断 A 在房间⾥的隐私。这就是不具备原⼦性的。 如何解决 给房间加⼀把锁A 进去就把⻔锁上其他⼈就进不来了。这样就保证了这段代码的原⼦性了。 有时也把这个现象叫做同步互斥表示操作是互相排斥的。 改为串行执行即可。
④内存可见性
可见性指, ⼀个线程对共享变量值的修改能够及时地被其他线程看到。 Java 内存模型 (JMM-JavaMemoryModel)Java虚拟机规范中定义了Java内存模型。 目的是屏蔽掉各种硬件和操作系统的内存访问差异以实现让Java程序在各种平台下都能达到⼀致的并发效果。 线程之间的共享变量存在于主内存 (Main Memory).每⼀个线程都有⾃⼰的 ⼯作内存 (Working Memory) . 当线程要读取⼀个共享变量的时候, 会先把变量从主内存拷⻉到⼯作内存, 再从⼯作内存读取数据. 当线程要修改⼀个共享变量的时候, 也会先修改⼯作内存中的副本, 再同步回主内存. 由于每个线程有自己的工作内存, 这些工作内存中的内容相当于同⼀个共享变量的 副本. 此时修改线程1 的⼯作内存中的值, 线程2 的⼯作内存不⼀定会及时变化. 而JMM 带来的问题是会导致线程非安全问题的发生。 import java.time.LocalDateTime;/*** 内存可见性问题*/
public class ThreadDemo18 {//全局变量类级别private static boolean flag true;public static void main(String[] args) {//创建子线程1Thread t1 new Thread(() - {System.out.println(线程 1开始执行 LocalDateTime.now());while(flag) {}System.out.println(线程 1结束执行 LocalDateTime.now());});t1.start();//创建子线程2Thread t2 new Thread(() - {//休眠1stry {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(线程 2修改 flag false LocalDateTime.now());flag false;});t2.start();}
}线程2早已把全局变量修改为另一个值而线程1一直在执行它并没有感知到全局变量flag的变化这就是内存可见性问题。
⑤指令重排序
有很多种
编译器指令重排序运行期指令重排序
编译器优化是一件非常复杂的事情其本质是调整代码的执⾏顺序保证原有逻辑不变的情况下提高程序执行效率。这在单线程下没问题但在多线程并发情况下容易出现混乱从而造成线程安全问题。