Java多线程之i++的安全性问题
来源:互联网 发布:数据管理软件开发 编辑:程序博客网 时间:2024/05/17 08:12
问题:
两个线程同时对i=0的数据分别进行i++一百次,结果出来并不是200。理论上来讲,结果最小值为2,最大值为200。
首先解释一下为什么会这样。
i++并非原子操作。执行过程中JVM从内存中把变量i的值取出来放到寄存器中,在寄存器中做加1操作,之后把寄存器中的值在写入到内存中。
值为2的情况:
线程A:从内存中读到i值为0,在寄存器中加1,还没有写入内存时线程B开始执行。
线程B:从内存中读到i值依然为0,线程B执行99次i++,这时候写入内存,此时内存中i=99,还没开始第100次读取内存中i的值,线程A开始执行。
线程A:把寄存器中的1写入到内存,此时内存中i=1,还没有开始第二次读取内存中i的值时,线程B开始执行。
线程B:把寄存器中的i=1读入到寄存器,进行加1操作,此时寄存器中值为2,还没有写入到内存中时,线程A开始执行。
线程A:把内存中的i=1读入到寄存器中加1然后写入内存,执行99次后,内存中的i=100,此时线程A执行完毕。线程B开始执行。
线程B:把寄存器中的i=2写入到内存中,线程B结束。
此时内存中的i=2。
值为200的情况:
线程完成从内存中读取到寄存器运算后写入内存这一个完整步骤。
其余情况类似。
解决这个问题的方法是加入同步块。
一开始我很愚蠢的这样写了
Count.java
package com.hnzhrh.Java7MultipleThread;public class Count { public int i=0;}
CountRunnable.java
package com.hnzhrh.Java7MultipleThread;public class CountRunnable implements Runnable{ Count count=null; public CountRunnable(Count count) { // TODO Auto-generated constructor stub this.count=count; } public synchronized void add(){ for(int j=0;j<100;j++){ count.i++; } } @Override public void run() { // TODO Auto-generated method stub add(); }}
Main.java
package com.hnzhrh.Java7MultipleThread;public class Main { public static void main(String[] args) throws InterruptedException { Count count=new Count(); Thread a =new Thread(new CountRunnable(count)); Thread b =new Thread(new CountRunnable(count)); a.start(); b.start(); a.join(); b.join(); System.out.println(count.i); }}
这个输出是错误的!完全错误的!一开始还怀疑这同步块并没有同步啊,后来发现是我傻逼了。
解释这个问题就要看一下JVM的内存模型:
JVM把内存分为两块,一块为线程栈,一块为堆。不同线程栈之间是不能相互访问的。比如线程栈A中有个本地变量,那么线程栈B是不能访问到该变量的。当然需要注意引用的情况,如果线程栈A中有个类的变量指向堆中的一个对象,线程栈B中也有个类的变量指向堆中的同一个对象,那么写操作是非线程安全的。
有了这点基础,我们可以分析一下我上面代码错到哪了。
我把add方法用了同步块,但是,我的add方法写在了实现了Runnable接口的CountRunnable类中,那么我创建了两个线程A和B,线程栈A中有这个方法,线程栈B中有这个方法,两个方法互不相干的对堆中的Count对象中的成员变量i进行操作,当然没有达到同步的作用。
只要做以下修改就可以了:
Count.java
package com.hnzhrh.Java7MultipleThread;public class Count { public int i=0; public synchronized void add(){ for(int j=0;j<100;j++){ i++; } }}
CountRunnable.java
package com.hnzhrh.Java7MultipleThread;public class CountRunnable implements Runnable{ Count count=null; public CountRunnable(Count count) { // TODO Auto-generated constructor stub this.count=count; } @Override public void run() { // TODO Auto-generated method stub count.add(); }}
- Java多线程之i++的安全性问题
- java多线程之线程的安全性(一)
- 多线程的安全性问题
- i++和++i的线程安全性问题
- epoll的多线程安全性问题
- 【Java多线程】线程的安全性
- 线程安全性---面试题--i++的线程安全性问题
- java多线程(二) 之 线程安全性
- i++的线程安全性
- i++的线程安全性
- i++的线程安全性
- i++的线程安全性
- i++的线程安全性
- 多线程fork的安全性
- 函数的多线程安全性
- servlet 多线程安全性问题分析
- Servlet多线程安全性有关问题
- Java语言自身的安全性问题
- SIGINT、SIGQUIT、 SIGTERM、SIGSTOP区别
- HDU 2012 素数判定 (Java)
- LA 4253 Archery
- XSS 攻击原理
- QNX系统BSP开发研发与应用,QNX技术解答-项目开发-软件开发
- Java多线程之i++的安全性问题
- Linux进程控制编程(五)
- Paper Note - Learning to Hash with Binary Deep Neural Network
- js-return返回多个值,通过对象的属性访问
- Tutorial: 344. Reverse String
- 浅识 VB.net
- 数组-选择排序-记录下标
- 自定义了一个标题栏类TitleLayout
- 基于vue.js开发的demo—天气APP