首先看一段简单的代码:
/**
* 两个线程同时操作一个共享变量,可能会出现线程安全问题
*/
@Slf4j
public class Thread1 {
private static int current;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 100 ; i++){
current++;
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 100 ; i++){
current--;
}
});
t1.start();
t2.start();
log.debug("current的值:"+current);
}
}
声明一下,这段代大家测试的时候不一定会出现线程安全问题,我测试很多次,只出现过一次错误,我理解的原因可能是线程电脑性能比较好,但是不出现不代表不存在。接下来我们就一起分析分析这段简单的代码。
分析:
1、首先大家应该理解current++和current--不是原子性操作,(current为静态变量),JVM会产生如下字节码指令:
getstatic current //获取静态变量current的值 取值-->主线程
currentconst_1 //准备常量 1 取值-->子线程
currentadd //自增 计算-->子线程
putstatic current //将修改后的值存入静态变量current 存值——>主线程
2、因此,产生线程安全性问题的地方就是JVM在执行字节码指令的时候,分为四步操作,可能A线程读取了静态变量current的值,而计算之后还为来得及提交到主线程,A线程的时间片就已经用完了,而此时B线程去读取主线程的current值就不是A线程计算后的结果,还是原始的值,这样就会导致两个线程计算时取的值是一样的,而正确的应该是A线程取B线程计算完写到主线程的值,或者B线程取A线程计算完存到主线程的值,这样才会抵消。
3、解决:synchronized对象锁
@Slf4j
public class Thread1 {
private static int current;
private static Object object = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 100 ; i++){
synchronized(object){
current++;
}
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 100 ; i++){
synchronized(object){
current--;
}
}
});
t1.start();
t2.start();
System.out.println(current);
}
}
完事儿~