JUC 并发编程

详细笔记可查看:https://blog.csdn.net/weixin_48412846/article/details/115681611

并发编程的本质:充分利用CPU的资源 

 

 

Synchronized 跟lock锁的区别:

  1. synchronized是内置的Java关键字,lock是一个Java类
  2. Synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁
  3. synchronized会自动释放锁,lock必须手动释放锁,如果不释放锁,就会造成死锁
  4. synchronized可重入锁 ,不可以中断,是非公平锁。lock也是可以重入锁,可判断锁,可以自己设置非公平
  5. 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();
        }
    }
}

 

 

 

线程常用辅助类:

  1. 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");
        }
  2. 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的核心数

 
四大函数式接口:
  

只有一个方法的接口统称为函数式接口
  1. Consumer:消费型接口 只有一个输入参数,没有输出参数
  2. SupplierL:供给型接口 没有输入参数,有一个输出参数
  3. Function:函数型接口,有一个输入参数,有一个输出参数
  4. 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虚拟机提供的轻量级的同步机制

  1. 保证可见性
       //加了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不知道主存中的值发生了变化
       }

     

  2. 不保证原子性
    原子性:不可分割
    线程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
        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);
        }
    
    这些类的底层都和操作系统挂钩,直接在内存中修改值,Unsafe类是一个很特殊的存在

  3. 禁止指令重排

       你写的程序,计算机并不是按照你写的那样去执行
    源代码->编译器优化->指令并行可能重排->内存系统可能重排->执行

       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(); 

 

各种锁的理解:
  
  公平锁:
  非公平锁:
  可重入锁(递归锁)
  自旋锁:
  死锁






posted @ 2022-05-05 15:29  码农小白David  阅读(56)  评论(0)    收藏  举报