赞
踩
多线程要保证并发线程正确执行,必须要保证三个特性。
原子性(互斥性)
可见性
有序性
synchronized可保证原子性和可见性。但不能保证有序性。
volatile可保证可见性和禁止指令重排。但不能保证原子性。
Lock接口间接借助了volatile关键字间接地实现了可见性和有序性。
初始flag = false
创建一个线程,循环判断flag标志,如果flag为true,则结束循环
在主线程中开启线程,然后休眠2秒,再修改falg为true
public class VisibilityTest implements Runnable{
public boolean flag = false;
@Override
public void run() {
while (true) {
if (flag) {
System.out.println("循环结束!");
break;
}
}
}
}
public class volatileTest { public static void main(String[] args) { VisibilityTest visibilityTest = new VisibilityTest(); new Thread(visibilityTest).start(); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } visibilityTest.flag = true; System.out.println("main线程结束"); } }
如果falg被修改为true,此时线程中的循环应该被退出
但是结果如下
main线程结束
(程序未停止...)
分析
虽然flag被修改为true,但是在子线程中并未获取到修改后的值
因为程序存在主存中,为了提高效率,线程执行后将值存储到了自己的缓存中,这样每次就不必去操作主存,从而提高效率。
主线程修改的是自己缓存中的值,而有可能还没来得及更新到主存中,也有可能子线程没来得及更新主存中的值,所以子线程看到的还是自己缓存中的内容,所以在主线程中修改了值,在子线程中并未看到。
那么如何解决呢?
只需要增加volatile关键字即可
public volatile boolean flag = false;
再次运行,程序就会立刻停止了
main线程结束
循环结束!
i++是原子操作吗?
答:不是,因为 i++ 分三步执行:取值、修改、替换
测试
public class Add {
private int num;
public int addNum() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return this.num++;
}
}
public class AddAddTest {
public static void main(String[] args) {
Add add = new Add();
for(int i = 0; i < 10; i++) {
new Thread(()-> System.out.print(add.addNum() + " ")).start();
}
}
}
预期结果应该是输出0到9不同的值,但是不保证顺序
结果
1 2 3 4 5 0 0 0 0 5
出现了连续4个0,和预期结果不符
分析
i++不是原子性操作,分三步:取值、修改、替换
在多线程下有可能会出问题,比如在A取到i的值为0的时候,还没等到修改,B也获取到了i的值为0,然后修改、替换i的值变为了1,此时又轮到了A执行,然后A修改、替换,也从0修改为1,此时就造成了错误
解决:
第一种方法:加锁
添加synchronized关键字,以保证操作同步
public class Add {
private int num;
public int addNum() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this) {
return this.num++;
}
}
}
第二种方式:使用原子变量
public class Add {
private AtomicInteger num = new AtomicInteger(0);
public int addNum() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取值并自增一
return num.getAndIncrement();
}
}
结果
5 2 0 3 8 9 1 4 7 6
0到9十个不同的数字,结果正确,说明此方法保证了自增操作是原子操作
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。