JUC 并发编程
详细笔记可查看:https://blog.csdn.net/weixin_48412846/article/details/115681611
并发编程的本质:充分利用CPU的资源
Synchronized 跟lock锁的区别:
- synchronized是内置的Java关键字,lock是一个Java类
- Synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁
- synchronized会自动释放锁,lock必须手动释放锁,如果不释放锁,就会造成死锁
- synchronized可重入锁 ,不可以中断,是非公平锁。lock也是可以重入锁,可判断锁,可以自己设置非公平
- synchronized适合锁少量的代码,lock适合锁大量的同步代码。
需要弄清什么是虚假唤醒。if跟while判断在虚假唤醒中有什么区别??
package com.fang.product; /** * 线程间的通信问题:生产者和消费者的问题! 等待唤醒 通知唤醒 * 线程交替执行 A B同时操作一个变量 * A num+1 * B num-1 */ public class A { public static void main(String[] args) { Data data = new Data(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); // new Thread(()->{ // for (int i = 0; i < 10; i++) { // try { // data.increment(); // } catch (InterruptedException e) { // e.printStackTrace(); // } // } // },"C").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); } } //等待 业务 通知 class Data{//数字 资源类 private int num = 0; public synchronized void increment() throws InterruptedException { if (num != 0){ //等待 this.wait(); } num++; System.out.println(Thread.currentThread().getName()+">="+num); //通知其他线程,我加一完毕了 this.notifyAll(); } public synchronized void decrement() throws InterruptedException { if (num == 0){ this.wait(); } num--; System.out.println(Thread.currentThread().getName()+">="+num); //通知其他线程,我减一完毕 this.notifyAll(); } }

加入C线程,执行结果:

存在问题虚假唤醒
将if改成while防止虚假唤醒.

结论:就是用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,
但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。
guc版生产者消费者问题
代码实现
package com.fang.product; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class B { public static void main(String[] args) { Data2 data2 = new Data2(); new Thread(()->{ for (int i = 0; i < 10; i++) { data2.increment(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data2.increment(); } },"C").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data2.decrement(); } },"B").start(); } } class Data2{//数字 资源类 private int num = 0; Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); // condition.await()等待 condition.signalAll()唤醒全部 public void increment() { lock.lock(); try { //业务代码 while (num != 0){ condition.await(); } num++; System.out.println(Thread.currentThread().getName()+">="+num); condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void decrement() { lock.lock(); try { //业务代码 while (num == 0){ condition.await(); } num--; System.out.println(Thread.currentThread().getName()+">="+num); condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }

线程状态随机
想要的执行循序A->B->C
condition实现精准通知唤醒
package com.fang.product; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * A->B->C */ public class C { public static void main(String[] args) { Data3 data3 = new Data3(); new Thread(()->{ for (int i = 0; i < 10; i++) { data3.printA(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data3.printB(); } },"B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data3.printC(); } },"C").start(); } } class Data3{ private Lock lock = new ReentrantLock(); private Condition condition1 = lock.newCondition(); private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition(); private int num =1; //1A,2B,3C public void printA() { lock.lock(); try { //业务,判断,执行,通知 while (num != 1){ condition1.await(); } System.out.println("aaaaaaaaaaaa"); //唤醒指定的人,B num =2; condition2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printB() { lock.lock(); try { while (num != 2){ condition2.await(); } System.out.println("bbbbbbbbbbbb"); //唤醒指定的人,B num =3; condition3.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printC() { lock.lock(); try { while (num != 3){ condition3.await(); } System.out.println("ccccccccccc"); //唤醒指定的人,B num =1; condition1.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
线程常用辅助类:
- CountDownLatch: countDownLatch.countDown() 方法 数量会减1 await() 方法 计数器归零之后才会继续往下执行。
/** * CountDownLatch 线程辅助类方法 数量会减1 await() 方法 计数器归零之后才会继续往下执行。 * 示例:7个人需要出门之后 门才能关上 (7个线程全部结束 才能执行后面的部分) */ public static void main(String[] args) { CountDownLatch countDownLatch=new CountDownLatch(7);//等待指定线程启动完毕才会结束 //必须要执行任务的时候 再使用 for (int i = 1; i <8; i++) { final int temp=i; new Thread(() -> { System.out.println(Thread.currentThread().getName()+"--->:"+temp); countDownLatch.countDown();//数量减1 },String.valueOf(i)).start(); } try { countDownLatch.await(); //等待计数器归零 然后再向下执行 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("close door"); }
- CyclicBarrier:加法计数器
/** CyclicBarrier 加法计数器 * 集齐七龙珠 召唤神龙 示例 * @param args */ public static void main(String[] args) { CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{ System.out.println("召唤神龙成功"); //只有集齐七颗龙珠 才能召唤神龙 }); for (int i = 1; i < 8; i++) { final int temp=i; new Thread(()->{ System.out.println(Thread.currentThread().getName()+":第"+temp+"颗龙珠"); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }).start(); } }
3. Semaphore:信号量
/** * 示例:抢车位 当有6辆车 需要停在三个车位的时候 123 车辆会进入停车位 等待123辆车其中有一辆车离开后 * 456其中会有一辆车自动进入 以此类推 */ public class Test { public static void main(String[] args) { Semaphore semaphore=new Semaphore(3); for (int i = 1; i < 7; i++) { new Thread(()->{ try { semaphore.acquire(); //得到车位 System.out.println(Thread.currentThread().getName()+"抢到车位"); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName()+"离开车位"); } catch (InterruptedException e) { throw new RuntimeException(e); }finally { semaphore.release(); //释放车位 } }).start(); } } }
semaphore.acquire():获得,假设如果已经满了,等待 ,等待被释放为止。
semaphore.release():释放,会将当前的信号量释放 +1 ,然后唤醒等待的线程!
作用:多个共享资源互斥使用,并发限流 控制最大线程数。
读写锁: ReadWriteLock 实现类 ReentrantReadWriteLock 一个文件只允许被一个单独的线程进行写入操作 不允许写入中途被其它线程插队,读取方法可以共享 因此会有读写锁的存在解决这个问题
BlockingQueue<E>:阻塞队列

ArrayBlockingQueue:
| 方式 | 抛出异常 | 有返回值,不抛异常 | 阻塞等待 | 超时等待 |
| 添加 | add() | offer() | put() | offer(,,) |
| 移除 | remove() | poll() | take() | poll(,) |
| 监测队首元素 | element | peek | 无 | 无 |
- 添加:向队列中添加元素
- 移除:向队列中取出元素
- 抛出异常:定义了队列长度的时候 当次队列中数据已经满了,再次向队列中添加数据(或者队列内数据元素已经全部取出,再次取出的时候,抛出异常)会抛出异常
- 不抛出异常:添加数据时会返回一个false或者true 表示数据向队列添加是否成功或者 移除队列是否成功
- 阻塞等待:当向队列中添加数据达到队列设置的长度时 队列会一直阻塞在此处,或者队列已经全部被移除 再次移除时队列也会继续阻塞在此处
- 超时等待:可以设置一个时间 当队列中已经满了 再次添加数据时 队列会阻塞在此处等待指定的时间后会自动退出,取出数据亦是如此
Class SynchronousQueue<E>:
同步队列:该队列只可允许添加一个元素 并且添加一个元素后必须取出其元素 不然会一直堵塞在此处
public static void main(String[] args) { // AbstractQueue blockingQueue=new ArrayBlockingQueue(3); //同步队列 和其他的blockingQueen不一样 SynchronousQueue 不存储元素 BlockingQueue<String> blockingQueue=new SynchronousQueue<String>(); new Thread(()->{ //存储数据 try { for (int i = 1; i <4 ; i++) { System.out.println("添加成功!"); blockingQueue.put(String.valueOf(i)); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { throw new RuntimeException(e); } } } catch (InterruptedException e) { throw new RuntimeException(e); } }).start(); new Thread(()->{ try { for (int i = 1; i <4 ; i++) { System.out.println("成功取到"+blockingQueue.take()); } } catch (InterruptedException e) { throw new RuntimeException(e); } }).start(); }
线程池:(重点)
线程池:三大方法、七大参数、四种拒绝策略
- 三大方法:
1.Excutors.newSingleThreadExcutor() 单个线程
2.Excutors.newFixedThreadPool(5) 创建一个固定的线程池大小
3.Excutors.newCatchedThreadPool() 可伸缩的,遇强则强 遇弱则弱
不推荐使用 Excutors工具类创建线程池 会存在资源利用率的问题 - 七大参数:
推荐使用 ThreadPoolExcutor创建线程池,构造方法所需 七个参数如下:int corePoolSize, 核心线程数
int maximumPoolSize, 最大线程数
long keepAliveTime, 多久后会释放线程
TimeUnit unit, 时间单位
BlockingQueue<Runnable> workQueue, 阻塞队列
ThreadFactory threadFactory, 线程创建工厂
RejectedExecutionHandler handler 拒绝策略
- 四种拒绝策略:
AbortPolicy:
CallerRunsPolicy:
DiscardOldestPolicy:
DiscardPolicy:
public static void main(String[] args) { //Executors工具类,三大方法 // ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程 // ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建一个固定大小得线程池 // ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩,线程数可变 //自定义线程池,工作 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 5, 3, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(), // new ThreadPoolExecutor.AbortPolicy()//银行满了还有人进来,不处理这个人,抛出异常 // new ThreadPoolExecutor.DiscardPolicy()//队列满了不会抛出异常,丢掉任务 // new ThreadPoolExecutor.CallerRunsPolicy()//哪里来的去哪里 new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试去和最早得竞争,也不会抛出异常 ); //最大承载:队列+max值 for (int i = 0; i < 8; i++) { //使用了线程池之后,使用线程池来创建线程 threadPoolExecutor.execute(()->{ System.out.println(Thread.currentThread().getName()+"ok"); }); } //线程池用完,程序结束,关闭线程池 try { threadPoolExecutor.shutdown(); } catch (Exception e) { e.printStackTrace(); } finally { } }
最大线程池应该如何定义:
1.cpu密集行,12条线程同时执行,几核心就是几,可以保证cpu的效率最高
2.io密集型>判断你的程序中十分耗io的线程
程序 15个大型任务 io十分暂用资源
System.out.println(Runtime.getRuntime().availableProcessors());//获得cpu的核心数
四大函数式接口:
只有一个方法的接口统称为函数式接口
- Consumer:消费型接口 只有一个输入参数,没有输出参数
- SupplierL:供给型接口 没有输入参数,有一个输出参数
- Function:函数型接口,有一个输入参数,有一个输出参数
- Predicate:断定型接口 :有一个参数 返回值只能是布尔值
Stream流式计算
ForkJoin 在jdk1.7,并行执行任务!提高效率
异步回调:类似ajax异步函数
Java也存在异步回调,当执行一个任务需要等待的时候,可以使用异步去干另外一件事情。
public static void main(String[] args) throws ExecutionException, InterruptedException { // //发起一个请求,没有返回值得异步回调 // CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{ // try { // TimeUnit.SECONDS.sleep(2); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName()+"run"); // }); // System.out.println("1111"); // //获取阻塞执行结果 // completableFuture.get(); //有返回值的异步回调 //ajax,成功和失败回调 //返回的是错误信息 CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{ System.out.println("completableFuture"+Thread.currentThread().getName()); int i = 10/0; return 1024; }); System.out.println(completableFuture.whenComplete((t, u) -> { System.out.println(t);//正常的返回结果 System.out.println(u);//错误信息java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero }).exceptionally((e) -> { System.out.println(e.getMessage());//java.lang.ArithmeticException: / by zero return 233; }).get()); }
Volatile: 是Java虚拟机提供的轻量级的同步机制
- 保证可见性
//加了volatile可以保证可见性,不加进入死循环 private volatile static int num = 0; public static void main(String[] args) throws InterruptedException {//main线程 new Thread(()->{//线程1 while (num == 0) { } }).start(); TimeUnit.SECONDS.sleep(1); num = 1; System.out.println(num); //程序一直在执行,线程1不知道主存中的值发生了变化 }
- 不保证原子性
原子性:不可分割
线程a在执行任务的时候,不能被打扰,也不能被分割,要么同时成功,要么同时失败
private volatile static int num = 0; public static void add() { num++; } public static void main(String[] args) { //理论上num结果应该为20000,加volatile还是不能加到2万,加Synchronized可以 for (int i = 0; i < 20; i++) { new Thread(()->{ for (int j = 0; j < 1000; j++) { add(); } }).start(); } while (Thread.activeCount()>2) { Thread.yield(); } System.out.println(Thread.currentThread().getName()+num); }
如果不使用sychronized跟Lock 如何保证原子性
num++;//不是原子性操作
使用原子类解决此问题:AtomicInteger这些类的底层都和操作系统挂钩,直接在内存中修改值,Unsafe类是一个很特殊的存在private volatile static AtomicInteger num = new AtomicInteger(); public static void add() { // num++; num.getAndIncrement();//AtomicInteger+1方法CAS效率高 } public void main(String[] args) { //理论上num结果应该为20000,加volatile还是不能加到2万,加Synchronized可以 for (int i = 0; i < 20; i++) { new Thread(()->{ for (int j = 0; j < 1000; j++) { add(); } }).start(); } while (Thread.activeCount()>2) { Thread.yield(); } System.out.println(Thread.currentThread().getName()+num); }
- 禁止指令重排
你写的程序,计算机并不是按照你写的那样去执行
源代码->编译器优化->指令并行可能重排->内存系统可能重排->执行
int x=1;
int y=1;
x=x+5;
y=x+x;
我们期望的是1234,但是可能是21344,1324 不可能是4123,==
处理器在执行指定重排的时候,考虑数据之间的依赖性可能造成影响的结果x,y,a,b默认是0
volitale可以避免指令重排:
内存屏障.cpu指令.作用
1.保证特定的操作执行循序
2.可以保证某些变量的内存可见性(利用这些特性,保证valitale实现了可见性)
彻底玩转单例模式:
饿汉式:
package 单例模式; //饿汉式单例 public class Hungry { //一上来就创建对象,可能会浪费空间 private byte[] data1 = new byte[1024*1024]; private byte[] data2 = new byte[1024*1024]; private byte[] data3 = new byte[1024*1024]; private byte[] data4 = new byte[1024*1024]; private Hungry() { } private final static Hungry HUNGRY= new Hungry(); public static Hungry getInstance() { return HUNGRY; } }
DCL饱汉式:
package 单例模式; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; //懒汉式单例模式 public class LazyMan { private static boolean qinjaing = false; private LazyMan() { System.out.println(Thread.currentThread().getName() + "ok"); synchronized (LazyMan.class) { // if (lazyMan != null) { // throw new RuntimeException("不要试图用反射破坏异常"); } if (qinjaing != false) { qinjaing = true; } else { throw new RuntimeException("不要试图用反射破坏异常"); } } //单线程下确实单例ok private volatile static LazyMan lazyMan; //双重检测锁模式 懒汉式单例模式 DCL懒汉式 public static LazyMan getInstance() { if (lazyMan == null) { synchronized (LazyMan.class) { if (lazyMan == null) { lazyMan = new LazyMan();//不是原子性操作, //1.分配内存空间 //2.执行构造方法,初始化对象 //3.把这个对象指向这个空间 //真实步骤可能执行132.此时lazyman还没被完成构造 } } } return lazyMan; } // //多线程并发 // public static void main(String[] args) { // for (int i = 0; i < 10; i++) { // new Thread(()->{ // LazyMan.getInstance(); // }).start(); // } // } // //反射破解使其不安全,破坏单例 // public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { // LazyMan instance = LazyMan.getInstance(); // //获得无参构造器 // Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null); // declaredConstructor.setAccessible(true); // LazyMan lazyMan = declaredConstructor.newInstance(); // //单例模式.LazyMan@15fbaa4 // //单例模式.LazyMan@1ee12a7 // System.out.println(instance); // System.out.println(lazyMan); // } //两个对象都使用反射再次破坏单例模式 public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { //通过反射破坏标志位qinjiang Field qinjaing = LazyMan.class.getDeclaredField("qinjaing"); qinjaing.setAccessible(true); //获得无参构造器 Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); LazyMan lazyMan = declaredConstructor.newInstance(); // qinjaing.set(lazyMan,false); LazyMan instance = declaredConstructor.newInstance(); //单例模式.LazyMan@15fbaa4 //单例模式.LazyMan@1ee12a7 System.out.println(instance); System.out.println(lazyMan); } }
静态内部类:
//静态内部类 public class Holder { private Holder() { } public static Holder getInstance() { return InnerClass.HOLDER; } public static class InnerClass { private static final Holder HOLDER = new Holder(); } }
此种方法因为反射的存在 同样不安全。因此使用枚举
package 单例模式; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; //enum是什么?本身也是一个class类 public enum EnumSingle { INSTANCE; public EnumSingle getInstance() { return INSTANCE; } } class Test{ public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { EnumSingle enumSingle1 = EnumSingle.INSTANCE; //反射不能破坏枚举 // EnumSingle enumSingle2 = EnumSingle.INSTANCE; Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class); declaredConstructor.setAccessible(true); EnumSingle enumSingle2 = declaredConstructor.newInstance(); System.out.println(enumSingle1); System.out.println(enumSingle2); //Cannot reflectively create enum objects // at java.lang.reflect.Constructor.newInstance(Constructor.java:417) } }
通过 Javap -p xxx.java 文件可以查看编译后的源码 会看到里面有一个private修饰的无参构造器(但其实无此构造器),使用反射执行此无参构造器时会报错 使用 javad.exe 将class文件编译成xxx.Java文件时可以看到
里面会有一个带有两个参数的构造器的存在,但使用反射时还是会抛出 Cannot reflectively create enum objects 异常 因为枚举无法被反射使用。

枚举类型最终反编译:

深入理解CAS:
cas:compareAndSet() 比较并且赋值,一般原子类(AtomicInteger)中会有此方法
理解:当一个值达到期望值时,会改变此值为最新值。


public class CASDemo { //CAS compareAndSet:比较并交换 public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(2020); //public final boolean compareAndSet(int expect, int update) //如果我期望的值达到了就更新,CAS是Cpu的并发原理 // 如果不是就一直循环,底层是自旋锁。 System.out.println(atomicInteger.compareAndSet(2020, 2021)); System.out.println(atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(2020, 2021)); System.out.println(atomicInteger.get()); // true // 2021 // false // 2021 } }
CAS缺点
1.循环会耗时
2.一次性只能保证一个共享变量的原子性
3.引发ABA问题
原子引用解决ABA问题
ABA问题(狸猫换太子)
import java.util.concurrent.atomic.AtomicInteger; public class CASDemo { //CAS compareAndSet:比较并交换 public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(2020); //对于平时写的sql,我们可以使用乐观锁 //public final boolean compareAndSet(int expect, int update) //如果我期望的值达到了就更新,CAS是Cpu的并发原理 // 如果不是就一直循环,底层是自旋锁。 //捣乱线程 System.out.println(atomicInteger.compareAndSet(2020, 2021)); System.out.println(atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(2021, 2020)); System.out.println(atomicInteger.get()); // true // 2021 // false // 2021 //期望线程,,并不知道2020已经被动过 System.out.println(atomicInteger.compareAndSet(2020,6666)); System.out.println(atomicInteger.get()); } }
解决ABA方法:原子引用
带版本号的原子引用
public static void main(String[] args) { //integer使用了对象缓存机制,超出了-128~127重新创建对象 //AtomicStampedReferencer如果泛型是一个包装类,注意对象引用问题 // AtomicStampedReference<Object> objectAtomicStampedReference = new AtomicStampedReference<>(2020,2021); //每次被动过版本号加一 //正常业务操作这里比较的是一个个对象 AtomicStampedReference<Integer> objectAtomicStampedReference = new AtomicStampedReference<>(1,1); //乐观锁的原理相同 new Thread(()->{ int stamp = objectAtomicStampedReference.getStamp();//获得版本号 System.out.println("a1->"+stamp); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } objectAtomicStampedReference.compareAndSet(1,2,objectAtomicStampedReference.getStamp(), objectAtomicStampedReference.getStamp()+1); System.out.println("a2->"+objectAtomicStampedReference.getStamp()); objectAtomicStampedReference.compareAndSet(2,1,objectAtomicStampedReference.getStamp(), objectAtomicStampedReference.getStamp()+1); System.out.println("a3->"+objectAtomicStampedReference.getStamp()); },"a").start(); new Thread(()->{ int stamp = objectAtomicStampedReference.getStamp();//获得版本号 System.out.println("b1->"+stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } objectAtomicStampedReference.compareAndSet(2,6,stamp,stamp+1); System.out.println("b2->"+objectAtomicStampedReference.getStamp()); },"b").start();
各种锁的理解:
公平锁:
非公平锁:
可重入锁(递归锁)
自旋锁:
死锁

浙公网安备 33010602011771号