多线程情况下,会出现两种线程安全的问题:可见性和原子性问题。具体针对这两种多线程情况下出现问题的解决方案如下:
(1)可见性:
//用volatile关键字屏蔽CPU缓存和指令重排序,保证线程可见性
public class VolatileTest {
boolean flag = true;//用这个会导致thread1线程不能读到被修改的flag值,thread1线程的循环永远不会结束
// volatile boolean flag = true;//用这个thread1线程可以读到被修改的flag值
int i = 0;
public static void main(String[] args) throws InterruptedException {
VolatileTest volatileTest = new VolatileTest();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
while (volatileTest.flag) {//当flag为false时循环结束
volatileTest.i++;
}
System.out.println("循环结束\n");
System.out.println("i为:"+volatileTest.i+"\n");
System.out.println("flag为:" + volatileTest.flag + "\n");
}
});
thread1.start();
Thread.sleep(3000);//休眠3秒
volatileTest.flag= false;//修改flag值
System.out.println("flag为:" + volatileTest.flag + "\n");
}
}
(2)原子性:
//利用CAS操作保证原子性,是一种乐观锁的思想(需要注意的是因为CAS需要自旋,可能会占用大量CPU)
public class AtomicTest {
public static void main(String[] args) {
/**
* 基本的原子操作
*/
AtomicInteger atomicInteger = new AtomicInteger(5);
atomicInteger.compareAndSet(5,6);
System.out.println(atomicInteger.get());
AtomicIntegerArray array = new AtomicIntegerArray(3);
array.set(1,12);
array.compareAndSet(1,12,13);
System.out.println(array.get(1));
/**
* 累加器
*/
LongAccumulator accumulator = new LongAccumulator((x,y)->{
//这里写累加的逻辑(x的累加器)
System.out.println("x:"+x+",y:"+y);
//把x+y的值赋给x了
return x+y;
},0);//identity是x的初始值
for (int i=0;i<3;i++){
accumulator.accumulate(1);//这里相当于每次累加1,这里是y的值
}
System.out.println("结果为:"+accumulator.get());
/**
* 带版本号的(解决ABA问题)
*/
int stamp = 0;//版本号
AtomicStampedReference<Integer> item = new AtomicStampedReference<Integer>(0,stamp);
item.compareAndSet(0,2,0,stamp+1);
System.out.println("最新的值为:"+item.getReference()+"\n");
System.out.println("最新的版本为:"+item.getStamp()+"\n");
}
}

本文深入探讨了多线程环境下常见的线程安全问题——可见性和原子性,并提供了具体的解决方案。通过使用volatile关键字确保变量在多线程间的可见性,以及利用CAS操作实现原子性更新,有效避免了并发编程中的数据不一致问题。

被折叠的 条评论
为什么被折叠?



