1. 原⼦类AtomicInteger的ABA问题谈谈?原⼦更新引⽤你知道吗?
2. 我们知道ArrayList是线程不安全,请编码写⼀个不安全的案例并给出解决⽅案
3. 公平锁/⾮公平锁/可重⼊锁/递归锁/⾃旋锁谈谈你的理解?请⼿写⼀个⾃旋锁
4. CountDownLath/CyclicBarrier/Semaphore使⽤过吗?
5. 阻塞队列知道吗?
6. 线程池⽤过吗?ThreadPoolExecutor谈谈你的理解?⽣产上你如何设置合理参数
7. 死锁编码及定位分析
二、ABA问题
所谓ABA问题,就是CAS算法实现需要取出内存中某时刻的数据并在当下时刻⽐较并替换,这⾥存在⼀ 个时间差,那么这个时间差可能带来意想不到的问题。
⽐如,⼀个线程B 从内存位置Value中取出2,这时候另⼀个线程A 也从内存位置Value中取出2,并且 线程A 进⾏了⼀些操作将值变成了5,然后线程A ⼜再次将值变成了2,这时候线程B 进⾏CAS操作发现 内存中仍然是2,然后线程B 操作成功。
尽管线程B 的CAS操作成功,但是不代表这个过程就是没有问题的。
有这样的需求,⽐如CAS,只注重头和尾,只要⾸尾⼀致就接受。
但是有的需求,还看重过程,中间不能发⽣任何修改,这就引出了 AtomicReference 原⼦引 ⽤。
三、AtomicReference原⼦引⽤
AtomicInteger对整数进⾏原⼦操作,如果是⼀个POJO呢?可以⽤AtomicReference来包装这个 POJO,使其操作原⼦化。
package thread;
public class AtomicReferenceDemo {
public static void main(String[] args) {
User user1 = new User("Jack",25);
User user2 = new User("Tom",21);
AtomicReference atomicReference = new AtomicReference();
atomicReference.set(user1);
System.out.println(atomicReference.compareAndSet(user1,user2)+"\t"+atomicReference.get()); // true
System.out.println(atomicReference.compareAndSet(user1,user2)+"\t"+atomicReference.get()); //false
}
}
四、ABA问题的解决(AtomicStampedReference 类似于时间 戳)
ThreadA 100 1 2020 2
ThreadB 100 1 111 2 100 3
使⽤AtomicStampedReference类可以解决ABA问题。
这个类维护了⼀个“版本号”Stamp,在进⾏CAS操作的时候,不仅要⽐较当前值,还要⽐较版本号。
只有两者都相等,才执⾏更新操作。
解决ABA问题的关键⽅法:
参数说明:
- V expectedReference, 预期值引⽤
- V newReference, 新值引⽤
- int expectedStamp, 预期值时间戳
- int newStamp, 新值时间戳
package thread;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
public class ABADemo {
static AtomicReference atomicReference = new AtomicReference(100);
static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(100, 1);
public static void main(String[] args) {
System.out.println("======ABA问题的产⽣======");
// 100->101->100
new Thread(() -> {
atomicReference.compareAndSet(100, 101);
atomicReference.compareAndSet(101, 100);
}, "t1").start();
// 100->2020
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1);
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicReference.compareAndSet(100, 2020) + "\t" + atomicReference.get().toString());
}, "t2").start();
try {
TimeUnit.SECONDS.sleep(2);
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" ======ABA问题的解决======");
// 100(1)->101(2)->100(3)
new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
// t3 第⼀次 版本号: 1
System.out.println(Thread.currentThread().getName() + "\t第⼀次 版本号: " + stamp);
try {
TimeUnit.SECONDS.sleep(1);
}
catch (InterruptedException e) {
e.printStackTrace();
}
//t3 第⼆次 版本号: 2
atomicStampedReference.compareAndSet(100,101, atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); System.out.println(Thread.currentThread().getName() + "\t第⼆次 版本号: " + atomicStampedReference.getStamp());
//t3 第三次 版本号: 3
atomicStampedReference.compareAndSet(101,100, atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); System.out.println(Thread.currentThread().getName() + "\t第三次 版本号: " + atomicStampedReference.getStamp());
}, "t3").start();
//
new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
// stamp = 1
// t4 第⼀次 版本号: 1
System.out.println(Thread.currentThread().getName() + "\t第⼀次 版本号: " + stamp);
try {
TimeUnit.SECONDS.sleep(3);
}
catch (InterruptedException e) {
e.printStackTrace();
}
boolean result=atomicStampedReference.compareAndSet(100,2020, stamp,stamp+1);
// t4 修改成功 与否:false 当前最新版本号3
System.out.println(Thread.currentThread().getName()+"\t修改成功 与否:"+result+" 当前最新版本号"+atomicStampedReference.getStamp());
// t4 当前实际值:100
System.out.println(Thread.currentThread().getName()+"\t当前实际值:"+atomicStampedReference.getReference());
}, "t4").start();
}
}