多线程引发数据不一致字节码层面理解
先看一段代码
public class TestSynchronized {
static int count;
static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread("t1") {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
count++;
}
}
};
Thread t2 = new Thread("t2") {
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
count--;
}
}
};
t1.start();
t2.start();
Thread.currentThread().sleep(1000);
System.out.println(count);
}
}
这里使用匿名内部类的方式创建两线程,并发修改TestSynchronized.count静态变量
编译出的字节码文件如上
TestSynchronized.class main方法的字节码指令为:
上述指令的的意思是先new 一个TestSynchronized$1对象(也就是匿名内部类对象)压入操作数栈中,复制一份并压入栈,将常量池中指向字符串"t1"的引用压入栈,调用TestSynchronized$1的init方法(弹出栈顶的TestSynchronized$1对象和"t1"),然后将创建的对象存入局部变量表索引为1的位置,之后以同样的方式创建TestSynchronized$2,然后分别调用他们的start方法,最后输出TestSynchronized.count。
重点在TestSynchronized$1 run方法的指令中
0行操作:将常量0压入栈
1:弹出栈存到局部变量表索引为1的位置(也就是给i赋值)
2:将表索引为1位置的值压入操作数栈
3:将1000压入栈
6:栈顶两数比较(即1与1000比较,1>1000的话跳转23行,否则继续执行)
9:获取TestSynchronized的静态变量count,并压入栈
12:将常量1压入栈
13:栈顶两数相加,并把结果压入栈
14:将栈顶元素赋给TestSynchronized的静态变量count
17:表中索引为1位置的值自增1
20:跳转到2
由上述指令可看出在9行中是获取TestSynchronized的count,14行将操作完的结果赋回给count。
就可能出现这种情况:假设当前count=1,线程1执行到了第9行获取了count的值1,但时间片结束,线程2获取时间片也执行到了第9行获取了count的值1,然后继续执行将操作完的值0赋给count,此时count的值为0,但线程1中操作数栈的值还是1,继续执行也将操作完的结果2赋给count。此时count的值为2,这样就发生了明明对count做了加一和减一操作,但值却变为了2。