线程安全问题是多线程中的重点与难点。所谓线程安全就是在CPU各种随机调度的顺序下,运行结果没有bug,能够符合预期的方式来执行。如果在多线程调度下,代码出现了bug,此时就认为线程是不安全的。
下面先来演示一下线程安全问题:
使用多线程使一个变量自增100000次:
class Count{
public int counter=0;
public void increase(){
counter++;
}
}
public class Test {
private static Count count=new Count();
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(()->{
for(int i=0;i<50000;i++){
count.increase();
}
});
Thread t2=new Thread(()->{
for(int i=0;i<50000;i++){
count.increase();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.print(count.counter);
}
}
运行三次的结果如下:
我们可以看到,运行出的结果是随机的,并不是期待的100000。这就是所谓的线程不安全。不安全的原因是什么呢?代码中进行的count++操作,底层是三条指令在CPU上完成的。1.把内存中的count加载到CPU寄存器中(load)。2.把CPU寄存器中的值进行++操作(add)。3.把寄存器中的值,写回到内存中(save)。
由于有两个线程同时执行这一代码逻辑,就会在真正执行的时候产生多种执行的排列顺序。
当代码在真正执行的时候,会有多种执行顺序。图1和图2虽是在多线程下执行的,但是和串行执行的代码效果一样,不会产生线程安全的问题。但是以图3为例:
首先是线程一将count加载到CPU的寄存器中:
然后线程2也将count加载到CPU的寄存器中: