在开发中一些共享变量存在线程安全问题,如何处理。我们通过实验去重现并找到解决方案。
1、直接使用Integer作为共享变量:
public static class Data {
private volatile Integer date = 0;
public void addDate() {
date = date + 1;
}
public Integer getDate() {
return date;
}
}
// 实验结果并不是每次都是1000,表明volatile关键字是不能保证线程安全的
private static void 无法保证线程安全() throws InterruptedException {
Data data = new Data();
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 100; j++) {
data.addDate();
}
}).start();
countDownLatch.countDown();
}
countDownLatch.await();
System.out.println("最终计算结果:" + data.getDate());
}
2、使用AtomicInteger实现线程安全的累加操作。
private static void 保证线程安全但无法处理ABA问题() throws InterruptedException {
AtomicInteger atomicInteger = new AtomicInteger(0);
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 100; j++) {
atomicInteger.incrementAndGet();
}
}).start();
countDownLatch.countDown();
}
countDownLatch.await();
System.out.println("最终计算结果:" + atomicInteger.get());
}
3、重现AtomicInteger在ABA问题下CAS验证通过的情况。
private static void CAS验证通过表明AtomicInteger无法保证ABA问题() throws InterruptedException {
AtomicInteger atomicInteger = new AtomicInteger(100);
Thread thread1 = new Thread(() -> {
atomicInteger.compareAndSet(100, 101);
atomicInteger.compareAndSet(101, 100);
});
Thread thread2 = new Thread(() -> {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean b = atomicInteger.compareAndSet(100, 101);
if (b) {
System.out.println("CAS验证成功");
} else {
System.out.println("CAS验证失败");
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("处理完成");
}
4、AtomicStampReference处理ABA问题。
private static void AtomicStampedReference通过版本戳处理ABA问题() throws InterruptedException {
AtomicStampedReference reference = new AtomicStampedReference<Integer>(100, 1);
Thread thread1 = new Thread(() -> {
reference.compareAndSet(100, 101, 1, 2);
reference.compareAndSet(101, 100, 2, 3);
});
Thread thread2 = new Thread(() -> {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean b = reference.compareAndSet(100, 101, 1, 2);
if (b) {
System.out.println("CAS验证成功");
} else {
System.out.println("CAS验证失败");
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("处理完成");
}