600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > 【Java 并发编程】线程操作原子性问题 ( 问题业务场景分析 | 使用 synchronized

【Java 并发编程】线程操作原子性问题 ( 问题业务场景分析 | 使用 synchronized

时间:2020-03-24 04:32:26

相关推荐

【Java 并发编程】线程操作原子性问题 ( 问题业务场景分析 | 使用 synchronized

文章目录

总结一、原子性问题示例二、线程操作原子性问题分析三、使用 synchronized 解决线程原子性问题

总结

原子操作问题 :线程中 , 对变量副本count进行自增操作 , 不是原子操作 ,首先从工作内存中读取变量副本到执行引擎 ( 操作数栈 ) 中 ,然后再 进行自增运算 ,最后写回到线程工作内存中 , 这是 333 个操作 , 如果变量 在这 333 个操作的空档时间进行了修改 , 那么就会产生无法预知的效果 ;

总结一下 :线程 A 的变量副本入操作数栈的时刻 , 该共享变量被线程 B 修改并且同步更新 , 此时入栈的这个变量自增是无效的 , 但是也算自增了 111 次 , 因此这里就丢失了 111 次计算机会 ;

一、原子性问题示例

开启 20 个线程 , 对某个线程共享int类型变量进行自增 , 每个线程自增 100001000010000 次 , 那么按照正常执行 , 20 个线程执行完毕后的变量值应该是 200000200000200000 ;

代码示例 :

public class Main {private volatile static int count = 0;private static void increase() {count++;}public static void main(String[] args) {for (int i = 0; i < 20; i ++) {new Thread(){@Overridepublic void run() {for (int i = 0; i < 10000; i ++) {increase();System.out.println(count);}}}.start();}}}

执行结果 :多运行几次 , 有的时候会出现结果不是200000的情况 , 这就是出现问题的情景 ;

二、线程操作原子性问题分析

上述程序中 , 将变量int count设置成volatile类型的 , 只能保证其 可见性 和 有序性 , 无法保证 线程操作的 原子性 ;

在线程中对int count = 0进行累加操作 , 首先将变量int count = 0加载到线程工作内存的变量副本中 , 这里创建了 20 个线程 , 就会有 20 个线程对应的工作内存空间 , 需要将count变量拷贝 20 份到相应的线程工作内存中 ;

有这样一种极端情况 , 当某个线程 A , 将 变量副本 加载到 线程执行引擎 时 , 就是 线程栈 中的 栈帧 的的 操作数栈 中 , 此时将要开始执行相关操作 , 在线程执行引擎没有执行之前 ,

与此同时 , 线程 B 修改了count副本变量 , 并进行了同步 , 主内存 , 包括 线程 A 的副本变量也已经更新了最新的值 ,

当前 线程栈中的栈帧中的操作数栈 中 , 还压着一个副本变量 , 虽然 该变量已经过时 , 该count++操作无效 , 这样就 丢失了 111 次count变量自增的操作 , 导致 最终输出的值是 199991999919999 ;

原子操作问题 :线程中 , 对变量副本count进行自增操作 , 不是原子操作 ,首先从工作内存中读取变量副本到执行引擎 ( 操作数栈 ) 中 ,然后再 进行自增运算 ,最后写回到线程工作内存中 , 这是 333 个操作 , 如果变量 在这 333 个操作的空档时间进行了修改 , 那么就会产生无法预知的效果 ;

总结一下 :线程 A 的变量副本入操作数栈的时刻 , 该共享变量被线程 B 修改并且同步更新 , 此时入栈的这个变量自增是无效的 , 但是也算自增了 111 次 , 因此这里就丢失了 111 次计算机会 ;

三、使用 synchronized 解决线程原子性问题

使用synchronized修饰increase方法 ;

private static void increase() {count++;}

方法 , 相当于在方法体重添加了synchronized代码块 ;

private static void increase() {synchronized (Main.class) {count++;}}

一旦某个线程执行synchronized方法或代码块中的代码 , 则当前线程持有互斥锁 , 只能由当前线程访问count变量 ;

代码示例 :

public class Main {private volatile static int count = 0;private synchronized static void increase() {count++;}public static void main(String[] args) {for (int i = 0; i < 20; i ++) {new Thread(){@Overridepublic void run() {for (int i = 0; i < 10000; i ++) {increase();System.out.println(count);}}}.start();}}}

执行结果 :

【Java 并发编程】线程操作原子性问题 ( 问题业务场景分析 | 使用 synchronized 解决线程原子性问题 )

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。