从1数到1000,有如下类:
class Calculate {
static int count = 0
def static inc() {
try {
Thread.sleep(1)
} catch (Exception e) {
}
count++
println count
}
}
开始数:
class Test {
public static void main(String[] args) {
for (int i in 1..1000) {
new Thread(new Runnable() {
@Override
void run() {
Calculate.inc()
}
}).start()
}
}
}
结果很接近但并不是预想的1000.
这要从jvm运行时刻的内存分配来解释这个问题。每个线程运行时都有一个线程栈,存放线程运行时的变量,当线程需要访问某一个对象时,会通过引用找到对应的堆内存地址,并把对应堆内存中的值加载到线程工作内存中,建立一个堆内存中的值的副本,在线程修改完值之后会再把副本写回到对象的堆内存中。
由于读取写回等操作并不是原子操作,当主内存的值发生改变时,并不会影响线程工作内存中的值,所以计算结果和预期并不一样。
这时候就要对自增操作进行加锁。
class Calculate {
static int count = 0
def static inc() {
synchronized (Calculate.class) {
try {
Thread.sleep(1)
} catch (Exception e) {
}
count++
println count
}
}
}
开始数:
class Test {
public static void main(String[] args) {
for (int i in 1..1000) {
new Thread(new Runnable() {
@Override
void run() {
Calculate.inc()
}
}).start()
}
}
}
还有volatile
修饰的变量并不能保证在并发情况的正确性。jvm虚拟机只能保证从主内存加载到线程工作内存中的值是最新的。
不加锁static volatile int count = 0
这样解决不了问题。