JUC多线程级高并发
- 一、请谈谈你对volatile的理解
- 1.volatile是Java虚拟机提供的`轻量级`的同步机制`※`
- 2. JMM你谈谈
- JMM三大特性(线程安全性获得保证)
- 验证volatile的可见性
- 验证volatile不保证原子性与解决办法
- 3.你在哪些地方用过volatile?
- 3.1单例模式DCL代码
- 3.2单例模式volatile分析
- 二 、CAS你知道吗?
- 1.比较并交换
- 2.CAS底层原理?如果知道,谈谈你对UnSafe的理解
- 3.CAS缺点
- 三、原子类AtomicInteger的ABA问题谈谈?原子更新引用知道吗?
- 1.**`ABA问题怎么产生的`**
- 2.**`原子引用`**
- 3.**`ABA问题的解决`**
- 四、我们知道ArrayList是线程不安全,请编写一个不安全的案例并给出解决方案。
- 1.解决方案一(new Vector、Collections.synchronizedList(new ArrayList<>()))
- 2.限制不可以使用Vector和Collections工具类解决方案2
- 3.集合不安全值写时复制CopyOnWriteArrayList
- 4.集合不安全之Set
- 5.集合类不安全之Map
- 五、公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解?请手写一个自旋锁
- 1.公平和非公平锁。
- 2.可重入锁(又名递归锁)
- 3.自旋锁
- 4.独占锁(写锁)/共享锁(读锁)/互斥锁
- 六、CountDownLatch/CyclicBarrier/Semaphore使用过吗?
- 七、阻塞队列知道吗?
- 八、线程池用过吗?ThreadPoolExcecutor谈谈你的理解?
- 九、死锁编码及定位分析?
- 1. 是什么
- 2.死锁产生的原因?
- 3.代码
- 4.解决
一、请谈谈你对volatile的理解
1.volatile是Java虚拟机提供的轻量级的同步机制※
※volatile三大特性※
- 1. 保证可见性
2. 不保证原子性:number++在多线程下是非线程安全的,如何不加synchronized解决?
- 3. 禁止指令重排
2. JMM你谈谈
JMM三大特性(线程安全性获得保证)
- 1.
可见性
- 2.
原子性
- 3.
有序性
重排1重排2禁止指令重排小总结
验证volatile的可见性
class MyData{
int number = 0;
public void addT060(){
this.number = 60;
}
}
/**
* 验证volatile的可见性
* 1.1 假如 int number = 0; number变量之前根本没有添加volatile关键字修饰,没有可见性
*/
public class VolatileDemo {
public static void main(String[] args) {
MyData myData = new MyData();
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t come in");
//暂停一会儿线程
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
myData.addT060();
System.out.println(Thread.currentThread().getName()+"\t update number value:"+myData.number);
},"AA").start();
//第二个线程就是我们的main线程
while(myData.number==0){
//main线程就一直在这里等待循环,直到number值不再等于零
}
System.out.println(Thread.currentThread().getName()+"\t mission is over,main get number value:"+myData.number);
}
}
给加了volatile
关键字之后volatile可以保证可见性,及时通知其它线程,主物理内存的值已经被修改
验证volatile不保证原子性与解决办法
class MyData{
volatile int number = 0;
public void addT060(){
this.number = 60;
}
//请注意,此时number前面加了volatile关键字修饰的,volatile不保证原子性
public void addPlusPlus(){
number++;
}
AtomicInteger atomicInteger = new AtomicInteger();
public void addMyAtomic(){
atomicInteger.getAndIncrement();
}
}
/**
* 1.验证volatile的可见性
* 1.1 假如 int number = 0; number变量之前根本没有添加volatile关键字修饰,没有可见性
* 1.2 添加了volatile,可以解决可见性问题。
* 2.验证volatile不保证原子性
* 2.1 原子性指的是什么意思?
* 不可分割,完整性,也即某个线程正在做某个业务时,中间不可以被加塞或被分割。需要整体完整。
* 要么同时成功,要么同时失败
* 2.2 volatile不保证原子性案列演示
*
* 2.3 why
*
* 2.4 如何解决原子性?
* 加synchronized 太消耗性能
* 使用我们juc下AtomicInteger
*
*/
public class VolatileDemo {
public static void main(String[] args) {
MyData myData = new MyData();
for (int i = 1; i <=20 ; i++) {
new Thread(()->{
for (int j = 1; j <= 1000; j++) {
myData.addPlusPlus();
myData.addMyAtomic();
}
},String.valueOf(i)).start();
}
//需要等待上面20个线程全部都计算完成以后,再用main线程取得最终的结果值看是多少?
while(Thread.activeCount()>2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+"\t int type,finally number value:"+myData.number);
System.out.println(Thread.currentThread().getName()+"\t AtomicInteger type,finally number value:"+myData.atomicInteger);
}
}
3.你在哪些地方用过volatile?
3.1单例模式DCL代码
public class SingletonDemo {
private static SingletonDemo instance = null;
private SingletonDemo(){
System.out.println(Thread.currentThread().getName()+"\t 我是构造方法SingletonDemo");
}
//DCL (Double Check Lock双端检锁机制)
public static SingletonDemo getInstance(){
if ( instance == null){
synchronized (SingletonDemo.class){
if (instance == null){
instance = new SingletonDemo();
}
}
}
return instance;
}
public static void main(String[] args) {
for (int i = 1; i <=10; i++) {
new Thread(()->{
SingletonDemo.getInstance();
},String.valueOf(i)).start();
}
}
}
3.2单例模式volatile分析
public class SingletonDemo {
private static volatile SingletonDemo instance = null;
private SingletonDemo(){
System.out.println(Thread.currentThread().getName()+"\t 我是构造方法SingletonDemo");
}
//DCL (Double Check Lock双端检锁机制)
public static SingletonDemo getInstance(){
if ( instance == null){
synchronized (SingletonDemo.class){
if (instance == null){
instance = new SingletonDemo();
}
}
}
return instance;
}
}
二 、CAS你知道吗?
1.比较并交换
如果线程的期望值与主物理内存的真实值一样,就修改为我的更新值,本次操作为true,修改。如果不一样,false,本次修改失败,重新获取主物理内存的值。
/**
* @Author leslie
* @Date 2021/9/10
*
* 1 CAS是什么? ===>compareAndSet
* 比较并交换
*/
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(5);
//main do thing ...
System.out.println(atomicInteger.compareAndSet(5,10)+"\t current data:"+atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(5,20)+"\t current data:"+atomicInteger.get());
}
}
2.CAS底层原理?如果知道,谈谈你对UnSafe的理解
atomicInteger.getAndIncrement()
UnSafe
CAS(自旋)是什么
unsafe.getAndAddInt
3.CAS缺点
循环时间长开销很大。
只能保证一个共享变量的原子操作。
引发出来ABA问题?
三、原子类AtomicInteger的ABA问题谈谈?原子更新引用知道吗?
1.ABA问题怎么产生的
2.原子引用
class User{
String userName;
int age;
public User(String userName, int age) {
this.userName = userName;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
public class AtomicReferenceDemo {
public static void main(String[] args) {
User z3 = new User("z3",20);
User li4 = new User("li4",30);
AtomicReference<User> atomicReference = new AtomicReference<>();
atomicReference.set(z3);
System.out.println(atomicReference.compareAndSet(z3,li4)+"\t"+atomicReference.get().toString());
System.out.println(atomicReference.compareAndSet(z3,li4)+"\t"+atomicReference.get().toString());
}
}
3.ABA问题的解决
public class ABADemo {//ABA问题的解决 AtomicStampedReference
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);
public static void main(String[] args) {
System.out.println("====以下ABA问题的产生====");
new Thread(()->{
atomicReference.compareAndSet(100,101);
atomicReference.compareAndSet(101,100);
},"t1").start();
new Thread(()->{
//暂停1秒钟t2线程,保证上面的t1线程完成一次ABA操作
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(atomicReference.compareAndSet(100,2020)+"\t"+atomicReference.get());
},"t2").start();
//暂停一会线程
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("====以下ABA问题的解决====");
new Thread(()->{
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t 第1次版本号:"+stamp);
//暂停1秒钟t3线程
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t 第2次版本号:"+atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t 第3次版本号:"+atomicStampedReference.getStamp());
},"t3").start();
new Thread(()->{
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t 第1次版本号:"+stamp);
//暂停3秒钟t4线程,保证上面的t3线程完成一次ABA操作
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
boolean result = atomicStampedReference.compareAndSet(100, 101, stamp, stamp + 1);
System.out.println(Thread.currentThread().getName()+"\t修改成功否:"+result+"\t当前最新实际版本号:"+atomicStampedReference.getStamp());
System.out.println(Thread.currentThread().getName()+"\t当前最新实际值:"+atomicStampedReference.getReference());
},"t4").start();
}
}
四、我们知道ArrayList是线程不安全,请编写一个不安全的案例并给出解决方案。
java.util.ConcurrentModificationException
1.解决方案一(new Vector、Collections.synchronizedList(new ArrayList<>()))
2.限制不可以使用Vector和Collections工具类解决方案2
3.集合不安全值写时复制CopyOnWriteArrayList
CopyOnWriteArrayList
写时复制
CopyOnWrite
容器即写时复制容器。往一个容器添加元素的时候,不直接往当前容器object[ ]添加,而是先将当前容器object[ ]进行copy,再将元容器的引用指向新的容器setArray(newElements)。这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
/**
* 集合不安全的问题
* ArrayList
*/
public class ContainerNotSafeDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
List<String> list1 = Collections.synchronizedList(new ArrayList<>());
List<String> list2 = new CopyOnWriteArrayList<>();
for (int i = 1; i <=30 ; i++) {
new Thread(()->{
list2.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
},String.valueOf(i)).start();
}
/**
* 1.故障现象
* java.util.ConcurrentModificationException
* 2.导致原因
* 并发争抢修改导致,参考我们的花名册签名情况。
* 一个人正在写入,另外一个同学过来抢夺,导致数据不一致异常。并发修改异常
*
* 3.解决方案
* 3.1 new Vector()
* 3.2 Collections.synchronizedList(new ArrayList<>())
* 3.3 new CopyOnWriteArrayList<>()
* 4.优化建议(同样的错误不犯第2次)
*/
}
}
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
4.集合不安全之Set
CopyOnWriteArraySet
public class ContainerNotSafeDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
Set<String> set1 = Collections.synchronizedSet(new HashSet<>());
Set<String> set2 = new CopyOnWriteArraySet<>();
for (int i = 1; i <=30 ; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
5.集合类不安全之Map
public class ContainerNotSafeDemo {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
Map<String,String> map1 = Collections.synchronizedMap(new HashMap<>());
Map<String,String> map2 = new ConcurrentHashMap<>();
for (int i = 1; i <=30 ; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,8));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
五、公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解?请手写一个自旋锁
1.公平和非公平锁。
是什么
两者区别
题外话
2.可重入锁(又名递归锁)
是什么
ReentrantLock和synchronized就是一个典型的可重入锁
可重入锁最大的作用就是避免死锁
3.自旋锁
/**
* 题目:实现一个自旋锁
* 自旋锁好处:循环比较获取知道成功为止,没有类似wait的阻塞。
*
* 通过CAS操作完成自旋锁,A线程先进来调用myLock方法自己持有5秒钟,B随后进来发现
* 当前有线程持有锁,不是null,所以只能通过自旋等待,直到A释放锁后B随后抢到
*/
public class SpinLockDemo {
// 原子引用线程
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"\t come in O(∩_∩)O");
while (!atomicReference.compareAndSet(null,thread)){
}
}
public void myUnLock(){
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread,null);
System.out.println(Thread.currentThread().getName()+"\t invoked myUnlock()");
}
public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(()->{
spinLockDemo.myLock();
//暂停一会线程
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
spinLockDemo.myUnLock();
},"AA").start();
//暂停一会线程,保证AA线程先启动
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(()->{
spinLockDemo.myLock();
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
spinLockDemo.myUnLock();
},"BB").start();
}
}
4.独占锁(写锁)/共享锁(读锁)/互斥锁
读写锁
读写锁文章链接
六、CountDownLatch/CyclicBarrier/Semaphore使用过吗?
文章地址链接
七、阻塞队列知道吗?
阻塞队列文章链接
八、线程池用过吗?ThreadPoolExcecutor谈谈你的理解?
线程池文章链接
九、死锁编码及定位分析?
1. 是什么
2.死锁产生的原因?
系统资源不充足
进程运行推进的顺序不合适
资源分配不当
3.代码
class HoldLockThread implements Runnable{
private String lockA;
private String lockB;
public HoldLockThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"\t 自己持有:"+lockA+"\t 尝试获得"+lockB);
try{ TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"\t 自己持有:"+lockB+"\t 尝试获得"+lockA);
}
}
}
}
public class DeadLockDemo {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new HoldLockThread(lockA, lockB),"ThreadA").start();
new Thread(new HoldLockThread(lockB,lockA),"ThreadB").start();
}
}
4.解决