ABA问题
你给的A不是原来的A,在一个线程里面原来是A,然后修改为B,再修改为A,这个A的数值都是一样的,但是A的对象已经不一样了,因为已经被修改了;
最好的例子就是AtomicXxx类了;
public class Test {
static AtomicInteger atomicInteger = new AtomicInteger(1);
public static final Object lock = new Object();
public static void main(String[] args) {
Thread threadA = new Thread(() -> {
synchronized (lock) {
if (atomicInteger.getAndIncrement() == 1) {
System.out.println("ThreadA before" + atomicInteger.get());
atomicInteger.getAndDecrement();
System.out.println("ThreadA after" + atomicInteger.get());
}
}
}, "ThreadA");
Thread threadB = new Thread(() -> {
synchronized (lock) {
if (atomicInteger.getAndIncrement() == 1) {
System.out.println("ThreadB before" + atomicInteger.get());
atomicInteger.getAndDecrement();
System.out.println("ThreadB after" + atomicInteger.get());
}
}
}, "ThreadB");
threadA.start();
threadB.start();
}
}
\
可以看到里面,线程A结束了,线程B会继续执行呢。
现在给这个加入版本的控制,使用AtomicStampedReference
static AtomicStampedReference<Integer> num = new AtomicStampedReference<Integer>(1,0);
public class Test {
static AtomicInteger atomicInteger = new AtomicInteger(1);
// 设置数值为1,版本为0
static AtomicStampedReference<Integer> num =new AtomicStampedReference<Integer>(1,0);
public static final Object lock = new Object();
public static void main(String[] args) {
Thread threadA = new Thread(()->{
synchronized (lock) {
// 期望的值是1,然后设置新值为2,期待版本为0,然后设置为1
if (num.compareAndSet(1, 2, 0, 1)) {
System.out.println("ThreadA before" + atomicInteger.get());
// 期望的值是2,然后设置新值为1,期待版本为1,然后设置为2
num.compareAndSet(2, 1,1, 2);
System.out.println("ThreadA after" + atomicInteger.get());
}
}
}, "ThreadA");
Thread threadB = new Thread(()->{
synchronized (lock) {
if (num.compareAndSet(1, 2,0, 1)) {
System.out.println("ThreadB before" + atomicInteger.get());
num.compareAndSet(2, 1,1, 2);
System.out.println("ThreadB after" + atomicInteger.get());
}
}
}, "ThreadB");
threadA.start();
threadB.start();
}
}
在线程A中执行后数值变成原来的数值,但是这个数值的版本号收到了改变;
在线程B中就无法获取相对应的数据了,这样线程B就没法执行了。
ABC问题
线程顺序执行
- 使用可重入锁进行显示的锁控制
- 使用CountDownLatch保证第一次执行的时候的顺序,也就是后面的
Thread.start()
public class ReentrantLockTest {
// 创建一个可重入的锁
static final ReentrantLock lock = new ReentrantLock();
// 创建三个条件,其实也就是三个队列,对应的是main方法里的A.B,C线程
static final Condition cA = lock.newCondition();
static final Condition cB = lock.newCondition();
static final Condition cC = lock.newCondition();
// 创建两个限制条件
static final CountDownLatch latchB = new CountDownLatch(1);
static final CountDownLatch latchC = new CountDownLatch(1);
public static void main(String[] args) {
Thread threadA = new Thread(() -> {
// 获取唯一的锁,让线程B和线程C都没办法操作
lock.lock();
try {
for (int i = 0; i < 10; i++) {
System.out.println("A");
if (i == 0) latchB.countDown();
// 唤醒B类线程。在我这也就是ThreadB
cB.signal();
// 将此线程(ThreadA)放入cA中,并且将线程状态设置为等待的状态
cA.await();
}
// 唤醒B类线程。
cB.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 将锁释放
lock.unlock();
}
}, "ThreadA");
Thread threadB = new Thread(() -> {
try {
// 在等待线程A将latchB的size设为0呢,也就是在线程A中执行的 latchB.countDown()
latchB.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取锁,让B单独执行,这个时候线程A已经处于等待状态了,而且线程C现在还在等待latchC的释放
lock.lock();
try {
for (int i = 0; i < 10; i++) {
System.out.println("B");
// 释放线程C的latchC
if (i == 0) latchC.countDown();
// 唤醒线程C
cC.signal();
// 将线程B设置为等待状态
cB.await();
}
cC.signal();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
// 解锁
lock.unlock();
}
}, "threadB");
Thread threadC = new Thread(() -> {
try {
// 在等待线程B将latchC的size设为0呢,也就是在线程A中执行的 latchC.countDown()
latchC.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
// 获取锁,让C单独执行,这个时候线程A已经处于等待状态了,而且线程B都处于等待的状态了
lock.lock();
for (int i = 0; i < 10; i++) {
System.out.println("C");
// 唤醒线程A
cA.signal();
// 线程C处于等待的状态
cC.await();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "threadC");
threadA.start();
threadB.start();
threadC.start();
}
}