Java并发下的count++问题

来源:互联网 发布:js获取自定义标签的值 编辑:程序博客网 时间:2024/06/07 18:45

需要深入理解volatile请看方腾飞的博客:
http://ifeve.com/volatile/


引用自 http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html


Java中的原子操作包括:

  1. 除long和double之外的基本类型的赋值操作
  2. 所有引用reference的赋值操作
  3. java.concurrent.Atomic.* 包中所有类的一切操作

count++不是原子操作,是3个原子操作组合:

  1. 读取主存中的count值,赋值给一个局部成员变量tmp
  2. tmp+1
  3. 将tmp赋值给count

可能会出现线程1运行到第2步的时候,tmp值为1;这时CPU调度切换到线程2执行完毕,count值为1;切换到线程1,继续执行第3步,count被赋值为1————结果就是两个线程执行完毕,count的值只加了1;
还有一点要注意,如果使用AtomicInteger.set(AtomicInteger.get()+1),会和上述情况一样有并发问题,要使用AtomicInteger.getAndIncrement()才可以避免并发问题


package com.lianglin.test;
public class VolatileTest {
static class MyObject{
static int mycount=0;
}
public static void inc() {
MyObject.mycount++;
}
public static void main(String[] args) {

//同时启动1000个线程,去进行i++计算,看看实际结果

for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
inc();
}
}).start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
};

//这里每次运行的值都有可能不同,可能为1000
System.out.println(“运行结果:Counter.count=” +MyObject.mycount);
}
}
每次输出值都是:1000

注意:

对于值引用来说,多线程操作的是变量的副本,操作完后刷新到主存中。而对于地址引用,多线程是通过地址操作的是同一个变量。volatitle关键字告诉编译器,直接去通过地址操作变量,而不是变量的副本

2016-01-22更新使用volatitle关键字:jvm虚拟机只是保证从主内存加载到线程工作内存的值是最新的,意即还是变量的副本.

一切重点都在于对于值引用来说,线程保存的是值的副本!如果count =1 ,
在走到第二步拷贝值到本地后,如果cpu切换到另一个线程完成了 count++ 那么内存中的count = 2
而此时第一个线程的副本还是1,此时再做count++并赋值给内存中的count,相当于并没有+1,只是重复赋值了一次

0 0