• Juc并发编程


    多线程进阶=>JUC并发编程

    1. 线程与进程

    并发、并行

    并发编程:并发、并行

    并发(多线程操作同一个资源)

    • CPU一核,模拟出来多个线程,快速交替

    并行

    • 多核CPU,多个线程同时执行,线程池

    2. lock 锁

    synchronized 与 lock 区别

    1. synchronized 内置的 java 关键字, Lock 是一个类
    2. synchronized 无法获取锁的状态,Lock 可以判断是否获取到了锁
    3. synchronized 会自动释放锁,lock 必须手动释放锁
    4. synchronized 可重入锁,不可以中断,非公平; lock,可重入锁,可以判断锁,非公平(可以自己设置)
    5. synchronized 适合少量的代码同步问题,lock 适合锁大量的同步代码。

    锁是什么,如何判断锁

    3.生产者消费者

    package JUC;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    /**
     * A执行完调用B,B执行完调用C,C执行完调用A
     * @author guojingbo
     * @date 2021-09-24 15:56
     * @param null
     * @return
     */
    public class pc {
        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 number = 1;
        public void printA(){
                lock.lock();
            try {
            //业务->判断->执行->通知
                while (number!=1){
                    //等待
                    condition1.await();
                }
                System.out.println(Thread.currentThread().getName()+"=>AAAAAAAA");
                //唤醒指定的人,B
                number = 2;
                condition2.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        public void printB(){
                lock.lock();
            try {
                while (number!=2){
                    condition2.await();
                }
                System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBB");
                number = 3;
                condition3.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        public void printC(){
                lock.lock();
            try {
                while (number!=3){
                    condition3.await();
                }
                System.out.println(Thread.currentThread().getName()+"=>CCCCCCCCCCCC");
                number = 1;
                condition1.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89

    4.8锁现象

    如何判断锁的是谁!永远知道什么是锁,锁到底锁的是谁!

    深刻理解我们的锁

    package JUC.lock8;
    
    import java.util.concurrent.TimeUnit;
    /**
     * 8锁就是关于锁的八个问题
     * 1. 标准问题,两个线程先打印还是发短信? 1/发短信 2/打电话 第一个线程先开启
     * 2.sendSms延迟 4 秒,两个线程先打印还是发短信? 1/发短信 2/打电话  两个线程先快先执行
     * 3. 先打印发短信还是 hello 1/hello 2/发短信  hello 没有加 synchronized 关键字
     * 4. 两个对象第一个 sendSms 第二个 call 哪个先 1.打电话 2、发消息 因为两个对象锁,并且打电话执行的更快
     * 5. 两个静态方法两个对象顺序一致哪个先 1.发消息 2.打电话 静态方法抢类锁,就算两个对象也还是一个类锁
     * 6.1个静态同步方法普通的同步方法,一个对象 1\打电话 2\发短信 因为抢的锁不同一个抢类锁一个抢对象锁
     * @author guojingbo
     * @date 2021-09-24 16:34
     *
     * @return
     */
    public class Phone {
        public static void main(String[] args) {
            int[] ints = new int[128];
    
            String a ="0";
            char c = a.charAt(0);
            ints[a.charAt(0)]++;
    //        ints[a.charAt(0)]=ints[a.charAt(0)]+1;
            for (int anInt : ints) {
                System.out.println(anInt);
            }
            System.out.println(c);
    
            Test1 test = new Test1();
            new Thread(()->{
                test.sendSms();
            }).start();
            //捕获
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(()->{
                test.call();
            }).start();
        }
    }
    class Test1{
        //synchronized 锁的对象是锁的调用者
        public static synchronized void sendSms(){
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("发短信");
        }
        public  synchronized void call(){
            System.out.println("打电话");
        }
        public void hello(){
            System.out.println("hello");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    5.集合类不安全

    package arithemetic;
    import java.util.concurrent.CopyOnWriteArrayList;
    public class Main {
    //    ConcurrentModificationException 并发修改异常
        /*
        解决方案
        1. List<String> list = new Vector<>();
        2. List<String> list = Collections.synchronizedList(new ArrayList<>());
        3. List<String> list = new CopyOnWriteArrayList<>();
        * */
        //copyOnWrite 写入时复制 COW 计算机设计领域的一种忧化策略
        //多个线程调用的时候,list 读取的时候,固定的,写入覆盖
        //在写入的时候避免覆盖,造成数据问题
        public static void main(String[] args) throws IOException, InterruptedException {
            List<String> list = new CopyOnWriteArrayList<>();
            for (int i = 0; i < 10; i++) {
                new Thread(() -> {
                    list.add(UUID.randomUUID().toString().substring(0, 5));
                    System.out.println(list);
                }, String.valueOf(i)).start();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    学习策略:1.先会用,2.货比三家,3.研究源码

    package JUC;
    
    import java.util.Set;
    import java.util.UUID;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    /**
     * 同理: concurrentModificationException
     * 解决方案
     *     1. Set<String> set = Collections.synchronizedSet(new HashSet<>()) ;
     *     2. Set<String> set = new CopyOnWriteArraySet();
     * @author guojingbo
     * @date 2021-09-24 19:58
     * @return
     */
    public class setTest {
        public static void main(String[] args) {
            Set<String> set = new CopyOnWriteArraySet();
            for (int i = 0; i < 10; i++) {
                new Thread(() -> {
                    set.add(UUID.randomUUID().toString().substring(0, 5));
                    System.out.println(set);
                }, String.valueOf(i)).start();
            }
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述

    ConcurrentHashMap 不能接受 null的 key 和 null 的 value 会抛出空指针异常

    6.Callable(简单)

    在这里插入图片描述

    1. 可以有返回值
    2. 可以抛出异常
    3. 方法不同,run(),call()
    new Thread(new Runnable).start()
    new Thread(new  FutureTask()).start()
    new Thread(new FutureTask(Callable)).start
    
    • 1
    • 2
    • 3
    package JUC.coallable;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class callableTest {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
    //        new Thread(new MyThread()).start();
            MyThread myThread = new MyThread();
            FutureTask futureTask = new FutureTask(myThread);
    
            new Thread(futureTask,"A").start();
            new Thread(futureTask,"B").start();//结果会被放入缓存提高效率
            String o =(String) futureTask.get();//这个 get 方法可能会产生阻塞,一般将其放在最后
            System.out.println(o);
        }
    }
    class MyThread implements Callable<String> {
    
    
        @Override
        public String call() throws Exception {
            return "234";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    细节:

    1. 有结果
    2. 可能需要等待,会阻塞

    7.常用的辅助类(重要)

    7.1 CountDownLatch

    在这里插入图片描述

    package JUC.add;
    
    import java.util.concurrent.CountDownLatch;
    
    // 计数器
    public class countDownLatchDemo {
        public static void main(String[] args) {
            // 必须要执行的任务的时候使用
            CountDownLatch countDownLatch = new CountDownLatch(6);
            for (int i = 0; i < 6; i++) {
                new Thread(()->{
                    System.out.println(Thread.currentThread().getName()+"Get Out");
                    countDownLatch.countDown();//数量减一
                },String.valueOf(i)).start();
            }
            try {
                countDownLatch.await();//等待计数器归零,然后再向下执行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Close Door");
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    减法计数器

    每次有线程调用 countDown 数量减一,假设计数器变为0 ,countDownLatch.await 调用这个函数后会判断是等于 0 如果不等于0会被阻塞,如果等于 0 会继续执行

    指定线程执行完毕,再执行操作

    7.2 CyclicBarrier

    在这里插入图片描述

    加法计数器

    package JUC.add;
    
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    
    public class CyclicBarrierDemo {
        public static void main(String[] args) {
            /**
             * 集齐七颗龙珠召唤神龙
             * @author guojingbo
             * @date 2021-09-25 09:29
             * @param args
             * @return void
             */
    
            CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
                System.out.println("召唤神龙成功");
            });
            for (int i = 0; i < 7; 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();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    CyclicBarrier 执行达到指定线程数再执行操作

    7.3 Semaphore

    在这里插入图片描述

    Semaphore: 同一时间只能有指定数量个得到线程

    public class SemaphoreDemo {
        public static void main(String[] args) {
            //线程数量:限流
            Semaphore semaphore = new Semaphore(3);
            for (int i = 0; i < 6; 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) {
                        e.printStackTrace();
                    }finally {
                        semaphore.release();
                    }
                }).start();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    semaphore.acquire()获得,假设已经满了,等待,直到有资源被释放

    semaphore.release()释放,将当前占有的信号量释放

    作用: 多个共享资源互斥的使用!并发限流,控制最大的线程数!

    8.读写锁

    ReadWriteLock

    在这里插入图片描述

    /**
     * 独占锁(写锁) 一次只能被一个线程占有
     * 共享锁(读锁) 一次能被多个线程占有
     * ReadWriteLock
     * 读-读   可以
     * 写-读  不可以
     * 写-写  不可以
     * @author guojingbo
     * @date 2021-09-25 10:32
     * @return
     */
    public class ReadWriteLockDemo {
        public static void main(String[] args) {
            MyCacheLock myCache = new MyCacheLock();
            for (int i = 0; i < 5; i++) {
                final int temp  = i;
                new Thread(() -> {
                    myCache.put(temp+"",temp+"");
                }, String.valueOf(temp)).start();
            }
            for (int i = 0; i < 5; i++) {
                final int temp  = i;
                new Thread(() -> {
                    myCache.get(temp+"");
                }, String.valueOf(temp)).start();
            }
    
        }
    }
    
    class MyCacheLock {
        private Map<String, String> resource = new HashMap<String, String>();
        //读写锁
        private ReentrantReadWriteLock lock  = new ReentrantReadWriteLock();
        //写
        public void put(String key, String value) {
           lock.writeLock().lock();
    
            try{
                System.out.println(Thread.currentThread().getName() + "写入" + key);
                resource.put(key, value);
                System.out.println(Thread.currentThread().getName() + "写入完毕");
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.writeLock().unlock();
            }
    
        }
    
        //读
        public void get(String key) {
            lock.readLock().lock();
    
            try{
                System.out.println(Thread.currentThread().getName() + "读取" + key);
                resource.get(key);
                System.out.println(Thread.currentThread().getName() + "读取完毕");
            }catch (Exception e){
                e.printStackTrace();
            }finally {
    
            }
    
        }
    }
    class MyCache {
        private Map<String, String> resource = new HashMap<String, String>();
    
        //写
        public void put(String key, String value) {
            System.out.println(Thread.currentThread().getName() + "写入" + key);
            resource.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入完毕");
        }
    
        //读
        public void get(String key) {
            System.out.println(Thread.currentThread().getName() + "读取" + key);
            resource.get(key);
            System.out.println(Thread.currentThread().getName() + "读取完毕");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    8.阻塞队列

    如果队列满了会阻塞

    如果队列是空的,必须阻塞生产

    在这里插入图片描述

    什么情况我们会使用阻塞队列:多线程并发处理,线程池!
    在这里插入图片描述

    学会使用队列

    添加、移除

    四组API

    方式抛出异常有返回值阻塞等待超时等待
    添加addofferputoffer(内容,时间,单位)offer("D",2,TimeUnit.SECONDS)
    移除removepooltakepool (时间,单位)
    判断队列头elementpeek
    /*抛出异常
         * @author guojingbo
         * @date 2021-09-25 11:31
         *
         * @return void
         */
        public static void test1(){
            //队列的大小
            ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
            System.out.println(arrayBlockingQueue.add("a"));
            System.out.println(arrayBlockingQueue.add("b"));
            System.out.println(arrayBlockingQueue.add("c"));
            System.out.println(arrayBlockingQueue.remove());
            System.out.println(arrayBlockingQueue.remove());
            System.out.println(arrayBlockingQueue.remove());
            System.out.println(arrayBlockingQueue.remove());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    SynchronousQueue 同步队列

    没有容量,进去一个元素必须等待取出来之后才能再往里面放一个元素!

    和其他的 BlockingQueue 不一样,SynchronousQueue 不存储元素 put 了一个元素,必须从里面先 take 取出来,否则不能再 Put 值

    9、线程池

    线程池:三大方法、7大参数、4种拒绝策略

    线程池的好处

    1. 降低资源的消耗
    2. 提高响应的速度
    3. 方便管理

    线程复用、可以控制最大并发数、管理线程

    在这里插入图片描述

    七大参数

        public ThreadPoolExecutor(int corePoolSize, //核心线程池的大小
                                  int maximumPoolSize, //最大核心池大小
                                  long keepAliveTime, //超时了没有人调用就会释放
                                  TimeUnit unit, // 超时单位
                                  BlockingQueue<Runnable> workQueue,// 阻塞队列
                                  RejectedExecutionHandler handler) { //拒绝策略
            this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                 Executors.defaultThreadFactory()//线程工厂,一般不用动
                 , handler);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    四种策略

    在这里插入图片描述

    /**
     * @author guojingbo
     * @date 2021-09-25 15:19
     * @return
     * AbortPolicy()   //满了如果还有人进来,就不处理这个人的,并且抛出异常
     * CallerRunsPolicy() //哪来的去哪里
     * DiscardPolicy() //队列满了,不会抛出异常,丢弃任务
     * DiscardOldestPolicy()//队列满了,尝试和最早的竞争,也不会抛出异常
     */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    小结与拓展

    调优

    /*
    最大线程该如何定义
    1. cpu 密集型 8条线程同时执行,保证 cpu 效率最高 Runtime.getRuntime().availableProcessors()
    2. io 密集型 >判断你的程序中十分耗费 io 的线程 一般为这个数量的两倍
    // 一个程序15个大型任务
    * */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    四大函数式接口

    函数式接口:只有一个方法的接口

    @FunctionalInterface
    public interface Runnable {
        public abstract void run();
    }
    //foreach  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • Consumer : 消费型接口

      • void accept(T t);
    • Supperlier:借给型接口

      • T get() 经过一系列的处理返回一个与原来一样类型的变量
    • Function<T,R> : 函数型接口

      • R apply(T t) 输入T 返回 R
    • Predicate: 断定型接口

      • boolean test(T t) 有一个输入参数,返回值只能是布尔值

    10. ForkJoin

    什么是 ForkJoin

    ForkJoin 在JDK 1.7,并行执行任务来提高效率的,大数据量!

    大数据:Map Reduce(大任务拆分为小任务)

    使用Stream 流

    LongStream.rangeClosed(0,10_0000_0000L).parallel().reduce(0,Long::sum);
    
    • 1

    11. 异步回调

    Future 是一个接口 CompletableFuture 是其一个实现类

      //异步回调
    //        CompletableFuture<Void> future = CompletableFuture.runAsync(()->{
    //            try {
    //                TimeUnit.SECONDS.sleep(2);
    //            } catch (InterruptedException e) {
    //                e.printStackTrace();
    //            }
    //            System.out.println(Thread.currentThread().getName()+"runAsYnc");
    //        });
    //      future.get() 有返回值的异步回调
            CompletableFuture<Integer> uCompletableFuture = CompletableFuture.supplyAsync(() -> {
                System.out.println(Thread.currentThread().getName()+"runAsYnc");
                return 1024;
            });
            uCompletableFuture.whenComplete((t,u)->{
                System.out.println(t+"=>");// 正常的返回结果
            }).exceptionally((e)->{
                String message = e.getMessage();// 不正常的返回结果
                return 404;
            });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    12.JMM

    淡淡对 Volatile 的理解

    Volatile 是 Java 提供轻量级的同步机制

    1. 保证可见性
    2. 不保证原子性
    3. 禁止指令重排

    什么是JMM

    JMM 是java 内存模型,不存在的东西,一个概念!约定

    关于JMM 一些同步的约定

    1. 线程解锁前,必须将共享变量立刻刷回主存
    2. 线程解锁前,必须读取主存中的最新值到工作内存中
    3. 加锁与解锁是同一把锁

    在这里插入图片描述

    在这里插入图片描述

    2、不保证原子性

    原子性:不可分割

    线程A 在执行的时候,不能被打扰,也不能被分割,要么同时成功,要么同时失败

    如果不加 lock 与 sychronized 怎么保证原子性

    使用原子类解决这个原子性问题,这些类底层都直接与操作系统挂钩!在内存中修改值。

    3、指令重排

    什么是指令重排

    源代码->编译器忧化的重排->指令并行也可能有一个重排->内存系统也会重排->执行

    处理器在指令重排的时候会考虑指令之间的依赖性

    volatile 可以避免指令重排

    内存屏障。CPU指令,作用:

    1. 保证特定的操作执行顺序
    2. 保证一些变量内存的可见性

    13、彻底玩转单例模式

    饿汉式 DCL懒汉式 ,深究!

    饿汉式

    package single;
    
    public class Hungry {
        private Hungry(){
    
        }
        private final static Hungry HUNGRY = new Hungry();
        public static Hungry getInstance(){
            return HUNGRY;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    懒汉式

    package single;
    
    public class LazyMan {
        private LazyMan(){}
        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.将这个对象指向这个空间
                         *  1->2->3
                         *  1->3->2
                         *  所以我们需要将这个对象加上 volatitle 保证指令不被重排
                         */
                    }
                }
            }
            return lazyMan;
        }
        // 多线程并发
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    enum

    package single;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    
    public enum  EnumSigle {
        INSTANCE;
        public EnumSigle getInstance(){
            return INSTANCE;
        }
    }
    class Test{
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            EnumSigle instance = EnumSigle.INSTANCE;
            Constructor<EnumSigle> declaredConstructor = EnumSigle.class.getDeclaredConstructor(String.class,int.class);
            declaredConstructor.setAccessible(true);
            EnumSigle instance1 = declaredConstructor.newInstance();
            System.out.println(instance1);
            System.out.println(instance);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    14.深入理解CAS

    什么是CAS

    compareAndSet :比较并交换

    java 无法操作内存 Java 可以调用 c++ c++可以调用内存,Unsafe 类就是java 的后门,通过这个类操作内存

    CAS 是CPU 的并发原语

    unSafe 类

    在这里插入图片描述

    在这里插入图片描述

    CAS ABA问题(狸猫换太子)

    CAS:比较当前工作内存中的值与主内存中的值,如果这个值是期望的,那么则执行操作,如果不是就一直循环!

    缺点:

    1. 循环会耗时
    2. 一次只能保证一个共享变量的原子性
    3. ABA 问题

    15.原子引用

    对应的思想:乐观锁

    带版本号的原子操作!

    public class CASDemo {
        //如果泛型是一个包装类,注意对象的引用问题
        public static void main(String[] args) {
            AtomicStampedReference<Object> atomicStampedReference = new AtomicStampedReference<>(1, 1);
            atomicStampedReference.compareAndSet
                    (2020, 2022, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    16、各种锁的理解

    1. 公平锁、非公平锁

    公平锁:不能够插队,必须先来后到

    非公平锁: 可以插队,cpu说的算

    在这里插入图片描述

    2.可重入锁

    拿到外面的锁,也就拿到了里面的锁

    3.自旋锁

    在这里插入图片描述

    package JUC.spinlock;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicReference;
    
    /**
     * 自旋锁
     *
     * @author guojingbo
     * @date 2021-09-27 14:36
     * @return
     */
    public class SplinlockDemo {
        AtomicReference<Thread>atomicReference = new AtomicReference<>();
    
        public void myLock(){
            Thread thread = Thread.currentThread();
            System.out.println(Thread.currentThread().getName()+"==>lock");
            // 自旋锁
            while (atomicReference.compareAndSet(null,thread)){
    
            }
        }
        public void myUnlock(){
            Thread thread = Thread.currentThread();
            System.out.println(Thread.currentThread().getName()+"====>MyUnlock");
            atomicReference.compareAndSet(thread,null);
        }
    }class Test{
        public static void main(String[] args) throws InterruptedException {
            SplinlockDemo lock = new SplinlockDemo();
            new Thread(()->{
                    lock.myLock();
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.myUnlock();
                }
            },"T1").start();
            TimeUnit.SECONDS.sleep(1);
            new Thread(()->{
                lock.myLock();
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.myUnlock();
                }
            },"T2").start();
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    4.死锁

    解决

    1. 使用jps -l定位进程号
      在这里插入图片描述

    2. 使用jstack进程号 找到死锁问题
      在这里插入图片描述

  • 相关阅读:
    Linux 中的文件简单说明
    Springboot基于web的游泳馆信息管理系统 毕业设计-附源码281444
    C++新特性 | bind & function
    Git实操图文详解系列教程(1)——Git原理与实践
    MVCC 过程中会加锁吗?
    Qt创建线程(继承于QThread的方法)
    ROS+PX4+mavros+qgc环境搭建笔记
    破解校园数字安全难点,联想推出智慧教育安全体系
    线性反馈移位寄存器的输出(未解出)
    linux ethtool 命令详解
  • 原文地址:https://blog.csdn.net/qq_53008149/article/details/125426706