线程安全问题
解决线程安全问题
1.首先观察线程不安全问题
- 如下代码:
本来预期结果n = 0;但是因为线程的安全问题,导致会出现其他结果,只有有不是预期结果的情况出现,我们就不能说它是安全的;
- 因为n++和n–的时候不具备原子性;
package 线程安全问题;
/**
* 这样线程存在很大的安全问题,因为结果不一定是预期结果
*/
public class ThreadDemo {
private static int n = 0;
private static final int COUNT = 100000;
static class Adder extends Thread{
@Override
public void run() {
for (int i = 0;i < COUNT;i++){
n++;
}
}
}
static class Suber extends Thread{
@Override
public void run() {
for (int i = 0;i < COUNT;i++){
n--;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Adder();
Thread t2 = new Suber();
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(n);
}
}
2.线程不安全原因
1.原子性
- 单一一条指令一定具备原子性——中间不会出现线程切换
- 多条指令之间发生了切换,也不会产生副作用;
比如刚才我们看到的 n++(就不具备原子性),其实是由三步操作组成的:
- 从内存把数据读到 CPU;
- 进行数据更新;
- 把数据写回到 CPU;
常见具有原子性的情况
//下面的这几个是具备原子性的
int a = 10;
boolean a =true;
byte/short/char/float
(因为JVM是以32 bit作为基本单位进行操作的)
//下面这俩个是不具备原子性的
long a = 10;
double a = 10.0;
2.可见性
- 为了提高效率,JVM在执行过程中,会尽可能的将数据在工作内存中执行,但这样会造成一个问题,共享变量在多线程之间不能及时看到改变,这个就是可见性问题。
3.代码顺序性(重排序)
1.什么是代码重排序
- 一段代码是这样的:
- 去前台取下 U 盘
- 去教室写 10 分钟作业
- 去前台取下快递
- 如果是在单线程情况下,JVM、CPU指令集会对其进行优化,比如,按 1->3->2的方式执行,也是没问题,可以少跑一次前台。这种叫做指令重排序。
2.代码重排序会给多线程带来的问题?
- 刚才那个例子中,单线程情况是没问题的,优化是正确的,但在多线程场景下就有问题了,什么问题呢。可能快递是在你写作业的10分钟内被另一个线程放过来的,或者被人变过了,如果指令重排序了,代码就会是错误的。
3.关于线程安全问题
- 1.线程之间没有数据共享,一定不会有线程安全问题;
- 2.即使有数据共享,但是只要没有修改操作,也不一定会有线程安全问题。