持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情
在编写多线程代码遇见问题时,错误往往属于三种特性之一
原子性
即一个或一系列操作,要么全部发生,要么都不发生
比如经典的银行转账,从银行账户 A 到账户 B 的货币转账。它包括两个操作,从账户 A 取出钱并将其保存到账户 B,如果这两个操作中的任何一个失败,钱既不会丢失也不会产生
可见性
线程之间的同步离不开可见性,若要保证一个线程的的结果需要被另一个线程感知到,即需要保证可见性
看一段代码
boolean flag = false;
//线程一执行
void start() {
while (!falg) {
}
}
//线程二随线程一执行后执行
void stop() {
flag = true;
}
这段代码可能导致一个结果,当线程二更改flag之后,线程一仍然在死循环,这可能是因为当线程二更改了flag的值后,并没有将其刷新到主存当中,而是存储在高速缓存中,为了避免这种情况,可以将flag的赋值语句改为volatile boolean flag = false
对 volatile 的所有操作都以单一顺序发生,每次读取都会按该顺序看到最后一次写入。换句话说,编译器无法阻止对 volatile 的读取看到另一个线程执行的写入。
有序性
代码执行顺序的正确性,只有保证了正确的执行顺序才能在并发环境下获得正确的结果
int a = 1;
int b = 2;
int c = 3;
这段代码在执行的时候,并不会按照我们编写的顺序执行,可能是乱序倒序等等,只要最后的结果一致,编译器会在优化的角度来重新编排代码的执行顺序,在单线程的情况下没有影响,但在多线程并发下会影响到正确性
int a = 0;
int b = 0;
//线程一运行
int value(){
int c = a;
int d = b;
return c+d;
}
//线程二运行
void change(){
a = 1;
b = 1;
}
value()的输出结果不一定是2,可能是1也可能是0,这一切都是因为没有保证有序性,可能在c读取a值的时候,因为重新编排的代码顺序,还没有执行a=1,可能先执行b=1,导致结果错误。
如何保证有序性呢?
- 为变量加上
volatile关键字 - 为函数加锁