• Java锁对象


    Java锁

    1. 对象头

    1.1 简介

    • 以32位的 JVM 为例,每个Java对象的对象头都包含了如下信息
    # 组成
    Mark Word:      锁的信息,hashcode,垃圾回收器标志
    Klass Word:     指针,包含当前对象的Class对象的地址,类对象来确定该对象是什么类型
    
    # 普通对象,占用8个字节,64位
                      Object Header (64 bits)
     Mark Word(32 bits)        Klass Word(32 bits)
    
    # 数组对象, 占用12个字节, 96位      包含额外的4个字节用来保存数组长度
                      Object Header (96 bits)
     Mark Word(32 bits)       Klass Word(32 bits)      Array Length (32 bits)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    1.2 Mark Word

    - 01/00/11:      代表是否加锁
    - age:           垃圾回收器标记
    
    • 1
    • 2

    image-20220926112525895

    2. Monitor监视器

    2.1 monitor

    # 由 《操作系统》提供,又叫监视器或管程
    # 操作系统可包含多个不同的Monitor
    # 包含三部分
       1. Owner:      保存当前获取到java锁的,线程指针
       2. EntryList:   保存被java锁阻塞的,线程指针
       3. WaitSet:     保存被java锁等待的,线程指针
    
    # synchronized 的java对象,该对象会被关联到一个monitor监视器,java对象头的Mark Word就被设置为 monitor监视器的地址
    - 被Synchronized修饰的java对象, 重量级锁,不公平锁
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    image-20220926113801439

    2.2 竞争步骤

    • java对象被synchronized修饰后
    • 当它获取到对象锁的时候,该对象就会被关联到monitor,java对象的Mark Word就会变为 prt_to_heavyweight_monitor, 即是用来保存重量级锁的地址,同时mark word中的01转变为10
    1. thread-1 通过synchronized获取到一个obj对象
      1.1 obj对象头信息(hashcode,age等)变为prt_to_heavyweight_monitor(30 bit)(monitor指针)
      1.2 obj对象头的锁状态变为 10(重量级锁)
      1.3 根据monitor指针,找到monitor,将Owner设置为thread-1
       
    2. thread-2 过来后,检查obj锁对象头
       2.1 发现该obj对象头的Mark Word的锁状态已经是重量级锁
       2.2 根据Mark Word中锁的地址检查到当Owner已经有其他线程了
       2.3 thread-2进入到EntryList,进行Block
      
    3. thread-1 执行完临界区代码后,
          3.1 monitor的Owner进行清空
          3.2 将owner中的当前线程的owner和obj对象头中的monitor地址再次交换
          3.3 monitor唤醒EntryList中其他线程
          3.4 其他在 EntryList 中等待的线程, 再次竞争对象锁,再次设置monitor的Owner
    
    a. synchronized(obj),就会有一个monitor监管该对象
    b. 同步代码块如果发生异常时候,也会将锁释放
    c. synchronized(obj), 必须关联到同一个obj,不然就不会指向同一个monitor
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    image-20220926120606596

    3. 常见锁

    3.1 轻量级锁

    • 锁对象虽被多个线程都来获取,但访问时间错开,不存在竞争
    • 轻量级锁对使用者 是透明的, 语法:syncronized
    • 当存在其他线程竞争的时候,自动升级为重量级锁

    3.2 锁重入

    • 锁重入: 一个线程在调用一个方法的时候,在方法调用链中,多次使用同一个对象来加锁

    image-20220928202550955

    # 创建锁记录
    - 线程在自己的工作内存内,创建栈帧,并在活动栈帧创建一个  《锁记录》  的结构
    - 锁记录: lock record address: 加锁的信息,用来保存当前线程ID等信息, 同时后续会保存对像锁的Mark Word
              Object Reference:  用来保存锁对象的地址
              00: 表示轻量级锁, 01代表无锁
              
    - 锁记录对象:是在JVM层面的,对用户无感知         
    - Object Body: 该锁对象的成员变量
    
    # 加锁cas-- compare and set
    # cas成功
    - 尝试cas交换Object中的 Mark Word和栈帧中的锁记录
    
    # cas失败
    - 情况一:锁膨胀,若其他线程持有该obj对象的轻量级锁,表明有竞争,进入锁膨胀过程,加重量级锁
    - 情况二:锁重入,若本线程再次synchronized锁,再添加一个Lock Record作为重入计数
    - 两种情况区分: 根据obj中保存线程的lock record地址来进行判断
    - null: 表示重入了几次
    
    # 解锁cas
    - 退出synchronized代码块时,若为null的锁记录,表示有重入,这时清除锁记录(null清除)
    - 退出synchronized代码块时,锁记录不为null,cas将Mark Word的值恢复给对象头
      同时obj头变为01无锁状态
    - 成功则代表解锁成功; 失败说明轻量级锁进入了锁膨胀
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    3.3 锁膨胀

    • 在尝试轻量级加锁时,cas无法成功
    • 可能因为:其他线程为此对象加上了轻量级锁(有竞争),这时进行锁膨胀,锁变为重量级锁
    • 轻量级锁没有阻塞机制,重量级锁有阻塞机制

    image-20220928205845097

    # 加锁
    - thread-0轻量级锁加锁成功
    - 当thread-1进行轻量级加锁时,thread-0已经为该对象加了轻量级锁,对应的java object是00
    - thread-1轻量级加锁失败,进入了锁膨胀流程
    
    # 锁膨胀
    - 为Object对象申请monitor锁,并让Object的mark word 指向重量级锁地址, 同时变为10(重量级锁)
    - 然后自己进入monitor的EntryList 进行 Block
    
    # 解锁
    - 当Thread-0 退出同步块时,使用cas将Mark Word的值恢复给对象头,失败进入重量级解锁流程
    - 按照Monitor地址找到Monitor,设置Owner为null,唤醒EntryList中BLOCKED线程
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.4 自旋优化-重量级锁

    - 一个线程的重量级锁被其他线程持有时,该线程并不会直接进入阻塞
    - 先本身自旋,同时查看锁资源在自旋优化期间是否能够释放   《避免阻塞时候的上下文切换》
    - 若当前线程自旋成功(即此时持有锁的线程已经退出了同步块,释放了锁),这时线程就避免了阻塞
    - 若自旋失败,则进入EntryList中
    
    • 1
    • 2
    • 3
    • 4
    智能自旋: 
    -  自适应的: Java6之后,对象刚刚的一次自旋成功,就认为自旋成功的概率大,就会多自旋几次
                反之,就少自旋几次甚至不自旋
    - java7之后不能控制是否开启自旋功能
    - 自旋会占用cpu时间,单核自旋就是浪费,多核自旋才有意义
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4. 锁消除

    - Java的 JIT, 即时编译器,对热点代码进行优化
    - 逃逸分析: JVM  是根据锁对象是否可以发生逃逸分析来判断
    - JVM默认开启锁消除机制
    - Java中锁消除默认是打开的,会根据代码中锁关联的对象是否能够逃逸决定是否优化
    - 关闭锁消除: java -XX: -EliminateLocks -jar demo.jar
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.1 消除

    package com.nike.erick.d01;
    
    public class Demo07 {
        
        public static void main(String[] args) {
            lockMethod();
            nonLockMethod();
        }
    
        /*虽然此时加了synchronized, 但是代码在执行的时候
         * 1. 并不存在多线程同步访问的场景,所以synchronized 被JIT优化掉了*/
        private static void lockMethod() {
            long startTime = System.currentTimeMillis();
            /*做成包装类,来增加时间*/
            Integer number = 0;
            for (int i = 0; i < 10000000; i++) {
                synchronized (new Object()) {
                    number++;
                }
            }
            System.out.println("Lock Method Times: " + (System.currentTimeMillis() - startTime));
        }
    
        private static void nonLockMethod() {
            long startTime = System.currentTimeMillis();
            Integer number = 0;
            for (int i = 0; i < 10000000; i++) {
                number++;
            }
            System.out.println("Non Lock Method Times: " + (System.currentTimeMillis() - startTime));
        }
    }
    
    • 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

    4.2 逃逸分析

    • 如果锁对象可能逃逸,那么就不会进行锁优化
    private static void lockMethod() {
            boolean flag = true;
            Object lock = new Object();
            
            new Thread(() -> {
                synchronized (lock) {
                    System.out.println("逃逸代码块");
                }
            }).start();
    
            long start = System.currentTimeMillis();
    
            /**
             * 上面锁逃逸,所以并不会进行锁消除
             */
            for (int i = 0; i < 100000000; i++) {
                synchronized (lock) {
                    flag = !flag;
                }
            }
            System.out.println("加锁:" + (System.currentTimeMillis() - start));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    5. 锁粒度细化

    5.1 单锁

    • 一间屋子两个功能:睡觉,学习,互不影响(不同的方法不会访问同一个资源)
    • 如果用一个屋子(一个对象锁)的话,并发度很低
    // 互不影响的功能
    
    package com.dreamer.multithread.day04;
    
    import java.util.concurrent.TimeUnit;
    
    public class Demo02 {
        public static void main(String[] args) throws InterruptedException {
    
            long start = System.currentTimeMillis();
            BigRoom room = new BigRoom();
    
            // 睡觉的一类线程
            Thread firstSleep = new Thread(() -> room.sleep());
            Thread secondSleep = new Thread(() -> room.sleep());
    
            // 工作的一类线程
            Thread firstWork = new Thread(() -> room.work());
            Thread secondWork = new Thread(() -> room.work());
    
            firstSleep.start();
            secondSleep.start();
            firstWork.start();
            secondWork.start();
    
            firstSleep.join();
            secondSleep.join();
            firstWork.join();
            secondWork.join();
    
            // 一共需要 2*2+2*2 = 8s
            System.out.println("total time:" + (System.currentTimeMillis() - start));
        }
    }
    
    class BigRoom {
    
        /*下面两个方法,永远不会在同一个时间调用,因此用同一把锁,浪费资源
                      或者说不会使用同一个共享资源*/
        public void sleep() {
            synchronized (this) {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public void work() {
            synchronized (this) {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    • 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

    5.2 多锁

    • 一个对象中,如果不同种类方法只会被同一种线程调用,则可以进行锁粒度细化
    • 如果多把锁,被一个方法同时使用了,可能造成死锁
    // 执行上面的方法,只需要4s
    class BigRoom {
    
        private Object sleepLock = new Object();
        private Object workLock = new Object();
    
        /*下面两个方法,永远不会在同一个时间调用,因此用同一把锁,浪费资源*/
        public void sleep() {
            synchronized (sleepLock) {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public void work() {
            synchronized (workLock) {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    • 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

    6. 活跃性

    • 因为某种原因,多线程代码一直在执行,不能正常结束

    6.1 死锁

    - 线程一:持有a锁,等待b锁
    - 线程二:持有b锁,等待a锁
    - 互相等待引发的死锁问题
    - 哲学家就餐问题
    - 定位死锁: 可以借助jconsole来定位死锁
    - 解决方法: 都按照相同顺序加锁就可以,但可能引发饥饿问题
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    package com.dreamer.multithread.day04;
    
    import java.util.concurrent.TimeUnit;
    
    public class Demo02 {
        public static void main(String[] args) {
            BigRoom room = new BigRoom();
            new Thread(() -> room.sleepAndWork()).start();
            new Thread(() -> room.workAndSleep()).start();
        }
    }
    
    class BigRoom {
    
        private final Object sleepLock = new Object();
        private final Object workLock = new Object();
    
        // 互相持有对方的锁
        public void sleepAndWork() {
            synchronized (sleepLock) {
                consumeTime();
                synchronized (workLock) {
                    System.out.println("睡醒---工作啦");
                }
            }
        }
    
        public void workAndSleep() {
            synchronized (workLock) {
                consumeTime();
                synchronized (sleepLock) {
                    System.out.println("工作后--要睡觉啦¬");
                }
            }
        }
    
        private void consumeTime() {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    • 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

    6.2 饥饿锁

    • 某个线程因为优先级太低,一直得不到cpu的执行

    6.3 活锁

    • 两个线程中互相改变对方结束的条件,导致两个线程一直运行下去
    • 可能会结束,但是二者会交替进行
    package com.dreamer.multithread.day04;
    
    public class Demo04 {
        private static int counter = 10;
    
        public static void main(String[] args) {
            new Thread(() -> {
                while (counter < 20) {
                    counter++;
                    System.out.println(" ++ 操作:" + counter);
                }
    
            }).start();
    
            new Thread(() -> {
                while (counter > 0) {
                    counter--;
                    System.out.println(" -- 操作:" + counter);
                }
            }).start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    Wait/Notify

    • 当线程执行任务的时候,发现条件不满足,则进行wait
    • 条件满足后,通过notify来再次执行任务

    1. 基本使用

    1.1 API

    wait()  notify()  notifyAll()
    Object类的方法,必须成为锁的owner时候才能使用
    
    # 当前线程进入WaitSet, 一直等待
    public final void wait() throws InterruptedException
    
    # 当前线程只等待一定时间,然后从 WaitSet 重新进入EntryList来竞争锁资源
    public final native void wait(long timeoutMillis) throws InterruptedException
    
    # 随便唤醒一个线程,进入到EntryList
    public final native void notify()
    
    # 唤醒所有的线程,进入到EntryList
    public final native void notifyAll()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    1.2 原理

    • Owner线程发现条件不满足,调用wait,即进入WaitSet变为WAITING状态
    • wait会释放当前锁资源
    1. BLOCK和WAITING的线程都处于阻塞状态,不占用cpu
    2. BLOCK线程会在Owner线程释放锁时唤醒
    3. WAITING线程会在Owner线程调用notify时唤醒,但唤醒后只是进入EntryList重新竞争锁
    
    • 1
    • 2
    • 3

    image-20220929091827634

    1.3 Demo

    package com.nike.erick.d02;
    
    import java.util.concurrent.TimeUnit;
    
    public class Demo01 {
    
        private static Object lock = new Object();
    
        public static void main(String[] args) throws InterruptedException {
            Thread firstThread = new Thread(() -> {
                synchronized (lock) {
                    try {
                        System.out.println("first thread coming");
                        /*进入WaitSet*/
                        lock.wait();
                        System.out.println("first thread ending....");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
    
            Thread secondThread = new Thread(() -> {
                synchronized (lock) {
                    try {
                        System.out.println("second thread coming");
                        lock.wait();
                        System.out.println("second thread ending....");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
    
            firstThread.start();
            secondThread.start();
    
            TimeUnit.SECONDS.sleep(2);
    
            /*唤醒线程必须也先获取到锁*/
            synchronized (lock) {
                /*唤醒多个线程*/
                lock.notifyAll();
            }
        }
    }
    
    • 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
    • 要wait或者notify,必须先获取到锁资源
    package com.nike.erick.d02;
    
    public class Demo02 {
        private static Object lock = new Object();
    
        public static void main(String[] args) {
            new Thread(() -> {
                try {
                    /*不能直接wait,要先获取到锁资源
                    * IllegalMonitorStateException */
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    1.4 wait VS sleep

    1. Wait 是Object的方法                     Sleep 是Thread 的静态方法
    2. Wait 必须和synchronized结合使用          Sleep 不需要
    3. Wait 会放弃当前线程的锁资源               Sleep 不会释放锁(如果工作时候带锁)
    4. 都会让出cpu资源,状态都是Timed-Waiting
    
    • 1
    • 2
    • 3
    • 4

    2.wait/notify正确使用

    • 循环wait: 防止虚假唤醒的问题,确保线程一定是执行完毕任务后才会结束
    // 工作线程
    synchronized(lock){
      while(条件不成立){
        lock.wait();
      }
      executeBusiness();
    }//其他线程唤醒
    synchronized(lock){
      // 实现上述条件
      lock.notifyAll();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.1. 单个线程wait

    package com.erick.multithread.d1;
    
    import java.util.concurrent.TimeUnit;
    
    public class Demo02 {
        private static final Object lock = new Object();
    
        private static boolean hasCigarette = false;
    
        public static void main(String[] args) throws InterruptedException {
            new Thread(() -> {
                synchronized (lock) {
                    /*while循环: 解决虚假唤醒问题*/
                    while (!hasCigarette) {
                        System.out.println("烟没到,休息会儿");
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
    
                    System.out.println("烟来了,开始干活");
                }
            }, "t1").start();
    
            for (int i = 0; i < 5; i++) {
                new Thread(() -> {
                    synchronized (lock) {
                        System.out.println("其他人开始干活");
                    }
                }).start();
            }
    
            TimeUnit.SECONDS.sleep(2);
            synchronized (lock) {
                hasCigarette = true;
                System.out.println("烟到了");
                lock.notify();
            }
        }
    }
    
    • 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

    2.2 多个线程wait

    • java 11 中,谁先进入WaitSet, notify先唤醒谁
     package com.erick.multithread.d1;
    
    import java.util.concurrent.TimeUnit;
    
    public class Demo03 {
        private static final Object lock = new Object();
    
        private static boolean hasCigarette = false;
    
        private static boolean hasDinner = false;
    
        public static void main(String[] args) throws InterruptedException {
            new Thread(() -> {
                synchronized (lock) {
                    while (!hasCigarette) {
                        System.out.println("烟没到,休息会儿");
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    System.out.println("烟来了,开始干活");
                }
            }, "t1").start();
    
            new Thread(() -> {
                synchronized (lock) {
                    while (!hasDinner) {
                        System.out.println("外卖没到,休息会儿");
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    System.out.println("外卖来了,开始干活");
                }
            }, "t2").start();
    
            for (int i = 0; i < 5; i++) {
                new Thread(() -> {
                    synchronized (lock){
                        System.out.println("其他人开始干活");
                    }
                }).start();
            }
    
            TimeUnit.SECONDS.sleep(2);
            synchronized (lock) {
                System.out.println("烟来了");
                hasCigarette = true;
                lock.notifyAll();
            }
        }
    }
    
    • 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

    3. 保护性暂停模式

    • 一个线程等待另一个线程的一个执行结果
    • Guarded Suspension Design Pattern
    - 一个结果需要从一个线程传递到另一个线程,让两个线程关联同一个GuardedObject
    - JDK中, join的实现,Future的实现,就是采用Guarded Suspension
    - 同步模式
    
    • 1
    • 2
    • 3

    image-20220929120241558

    3.1 无限等待

    package com.erick.multithread.d1;
    
    import java.util.concurrent.TimeUnit;
    
    public class Demo04 {
    
        private static GuardedResponse response = new GuardedResponse();
    
        public static void main(String[] args) {
            new Thread(() -> System.out.println(response.obtainResult())).start();
    
            new Thread(() -> response.populateResult()).start();
        }
    }
    
    class GuardedResponse {
    
        private Object result;
    
        public synchronized Object obtainResult() {
            while (null == result) {
                try {
                    System.out.println("暂未获取到资源");
                    this.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("已经获取到资源");
            return result;
        }
    
        public synchronized void populateResult() {
            result = heavyWork();
            this.notifyAll();
        }
    
        private Object heavyWork(){
            try {
                TimeUnit.SECONDS.sleep(3);
                return new Object();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    • 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

    3.2 超时等待

    • 如果获取结果不到,那么就返回
    package com.nike.erick.d04;
    
    import java.util.ArrayList;
    import java.util.concurrent.TimeUnit;
    
    public class Demo06 {
        private static GuardResponse guardResponse = new GuardResponse();
    
        public static void main(String[] args) {
            new Thread(() -> System.out.println(guardResponse.getResponse(2000))).start();
    
            new Thread(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                guardResponse.setResponse(new ArrayList<>());
            }).start();
        }
    }
    
    class GuardResponse {
        private Object response;
    
        public Object getResponse(long timeoutMills) {
            synchronized (this) {
                /*开始时间*/
                long startTime = System.currentTimeMillis();
                /*经过了多长时间*/
                long passedTime = 0;
    
                while (response == null) {
                    long leftTime = timeoutMills - passedTime;
                    /*如果经过的时间大于了等待时间,则退出*/
                    if (leftTime <= 0) {
                        break;
                    }
                    /*动态等待*/
                    try {
                        this.wait(leftTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    /*因为在wait时候可能被虚假唤醒*/
                    passedTime = System.currentTimeMillis() - startTime;
                }
                return response;
            }
        }
    
        public void setResponse(Object response) {
            synchronized (this) {
                this.response = response;
                this.notify();
            }
        }
    }
    
    • 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

    3.3 Join原理

    • 底层就是使用了保护性暂停模式

    4. 生产者消费者

    • 如果有结果从一类线程不断的传递到其他类线程,可以使用消息队列(生产者消费者)
    • 多个生产者及多个消费者, 阻塞队列, 异步消费
    • 消息队列,先入先得,有容量限制,满时不再添加消息,空时不再消费消息
    • JDK中各种阻塞队列,就是用的这种方式

    image-20220929180221221

    package com.erick.multithread.d1;
    
    import java.util.LinkedList;
    
    public class Demo05 {
        public static void main(String[] args) {
            MessageBroker<String> messageBroker = new MessageBroker<>(5);
            for (int i = 0; i < 10; i++) {
                new Thread(() -> messageBroker.sendMessage("hello:" + Thread.currentThread().getName())).start();
            }
    
            for (int i = 0; i < 2; i++) {
                new Thread(() -> messageBroker.consumeMessage()).start();
            }
        }
    }
    
    class MessageBroker<T> {
        /*阻塞队列大小*/
        private int capacity;
    
        private LinkedList<T> blockingQueue = new LinkedList();
    
        public MessageBroker(int capacity) {
            this.capacity = capacity;
        }
    
        public synchronized void sendMessage(T message) {
            while (blockingQueue.size() >= capacity) {
                try {
                    System.out.println("队列已满,请稍后再发送消息");
                    this.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
    
            blockingQueue.addFirst(message);
            System.out.println("添加成功:" + Thread.currentThread().getName());
            this.notifyAll(); // 唤醒所有的生产者和消费者线程,不用担心虚假唤醒问题
        }
    
        public synchronized T consumeMessage() {
            while (blockingQueue.size() <= 0) {
                try {
                    System.out.println("当前暂无消息,请稍后再消费");
                    this.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
    
            T message = blockingQueue.removeLast();
            this.notifyAll();
            return message;
        }
    }
    
    • 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

    5. Park/Unpark

    5.1. 基本使用

    • 先park,再unpark
    • park后是waiting状态,会释放锁
    # 暂停当前线程
    java.util.concurrent.locks.LockSupport
    
    # 在哪个线程中使用,就暂停哪个线程
    public static void park()
    
    # 恢复一个线程
    public static void unpark(Thread thread)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    package com.dreamer.multithread.day04;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.LockSupport;
    
    public class Demo01 {
        public static void main(String[] args) throws InterruptedException {
            Thread slaveThread = new Thread("slave-thread") {
                @Override
                public void run() {
                    System.out.println("prepare for PARK....");
                    LockSupport.park();
                    System.out.println("PARK ended");
                }
            };
            slaveThread.start();
    
            TimeUnit.SECONDS.sleep(2);
    
            LockSupport.unpark(slaveThread);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    5.2. 先unpark后park

    • 先unpark,再park,线程就不会停下来了
    package com.dreamer.multithread.day04;
    
    import java.util.concurrent.locks.LockSupport;
    
    public class Demo01 {
        public static void main(String[] args) {
            Thread slaveThread = new Thread("slave-thread") {
                @Override
                public void run() {
                    LockSupport.unpark(Thread.currentThread());
                    System.out.println("prepare for PARK....");
                    LockSupport.park();
                    System.out.println("PARK ended");
                }
            };
            slaveThread.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    5.3 wait/park

    # 二者都会使线程进入waitset等待,都会释放锁
    
    wait/notify是Object的方法                    park/unpark是LockSupport
    wait/notify 必须和synchronized结合使用        park/unpark不必
    wait/notify 顺序不能颠倒                      park/unpark可以颠倒
    wait/notify 只能随机唤醒一个或者全部唤醒         park/unpark可以指定一个线程唤醒
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ReentryLock

    1. 基本特性

    1.1 可重入锁

    • 一个线程已经获取锁,因为该线程是该锁主人。第二次获取该锁时,依然可以获取到该锁
    • 不可重入:第二次获取就会造成死锁,部分锁就是这种情况
    package com.erick.multithread.d2;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Demo01 {
        public static void main(String[] args) {
            FirstTest firstTest = new FirstTest();
            firstTest.firstMethod();
        }
    }
    
    
    class FirstTest{
        private ReentrantLock lock = new ReentrantLock();
    
        public void firstMethod(){
            try {
                lock.lock();
                System.out.println("first method coming");
                secondMethod();
            }finally {
                /*unlock必须放在finally中,保证锁一定可以释放*/
                lock.unlock();
            }
        }
    
        public void secondMethod(){
            try{
                lock.lock();
                System.out.println("second method coming");
            }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

    1.2 可打断锁

    • 被动的方式: 避免一直等待带来的死锁问题
    - 没有其他线程争夺锁,则正常执行
    - 有竞争时,线程就会进入EntryList,但是可以被打断
    - 其他线程先获取锁,执行一段时间后,等待获取锁的线程 《打断等待》
    
    • 1
    • 2
    • 3
    无竞争
    package com.erick.multithread.d2;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Demo02 {
        private static ReentrantLock lock = new ReentrantLock();
    
        public static void main(String[] args) {
    
            new Thread(() -> {
                /*第一个try表示可以被打断*/
                try{
                    lock.lockInterruptibly();
                } catch (InterruptedException e) {
                    System.out.println("锁被打断了");
                    throw new RuntimeException(e);
                }
                /*第二个try表示释放锁*/
                try{
                    doBusiness();
                }finally {
                    lock.unlock();
                }
            }).start();
        }
    
        private static void doBusiness(){
            System.out.println("do business logic");
        }
    }
    
    • 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
    有竞争
    package com.erick.multithread.d2;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Demo03 {
        private static ReentrantLock lock = new ReentrantLock();
    
        public static void main(String[] args) {
    
            Thread firstThread = new Thread(() -> {
                /*获取锁的时候,可以被打断,就结束当前等待过程*/
                try {
                    System.out.println("first-thread 开始等待锁");
                    lock.lockInterruptibly();
                } catch (InterruptedException e) {
                    System.out.println("first-thread 锁被打断");
                    throw new RuntimeException(e);
                }
    
                try {
                    doBusiness();
                } finally {
                    lock.unlock();
                }
            });
    
            Thread secondThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        lock.lock();
                        /*不会释放锁*/
                        sleep(2);
                        firstThread.interrupt();
                        System.out.println("second-thread完成业务");
                    } finally {
                        lock.unlock();
                    }
                }
            });
    
            secondThread.start();
            sleep(1);
            firstThread.start();
        }
    
        private static void doBusiness() {
            System.out.println("do business logic");
        }
    
        private static void sleep(int seconds) {
            try {
                TimeUnit.SECONDS.sleep(seconds);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    • 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

    1.3 公平锁

    # 1. 不公平锁
    - 当一个线程持有锁的时候,其他线程进入锁的 EntryList
    - 当线程释放锁的时候,其他线程一拥而上,而不是按照进入的顺序先到先得
    
    # 2. 公平锁: 通过ReentranLock实现
    - 默认是非公平锁,传参为true,公平锁
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    package com.erick.multithread.d2;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Demo04 {
    
        private static ReentrantLock lock = new ReentrantLock(true);
    
        public static void main(String[] args) {
            new Thread(() -> {
                try {
                    lock.lock();
                    sleep(5);
                } finally {
                    lock.unlock();
                }
            }).start();
    
            sleep(1);
    
            for (int i = 0; i < 3; i++) {
                new Thread(() -> {
                    try {
                        lock.lock();
                        System.out.println(Thread.currentThread().getName() + " running");
                    } finally {
                        lock.unlock();
                    }
                }).start();
    
                sleep(1);
            }
        }
    
        private static void sleep(int seconds) {
            try {
                TimeUnit.SECONDS.sleep(seconds);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    • 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

    1.4 超时锁

    • 避免死锁: 主动方式来避免一个线程一直等待锁资源,带来的死锁问题
    不带超时
    • 正常获取到锁
    package com.erick.multithread.d2;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Demo05 {
        private static ReentrantLock lock = new ReentrantLock();
    
        public static void main(String[] args) {
            new Thread(() -> {
                boolean hasLock = lock.tryLock();
                if (!hasLock) {
                    System.out.println("没有获取到锁,放弃");
                    return;
                }
    
                try{
                    executeBusiness();
                }finally {
                    lock.unlock();
                }
            }).start();
        }
    
        private static void executeBusiness(){
            System.out.println("do business logic");
        }
    }
    
    • 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
    • 最终没获取到锁
    package com.erick.multithread.d2;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Demo06 {
        private static ReentrantLock lock = new ReentrantLock();
    
        public static void main(String[] args) {
            new Thread(() -> {
                try{
                    lock.lock();
                    sleep(5);
                }finally {
                    lock.unlock();
                }
            }).start();
    
            new Thread(() -> {
                boolean hasLock = lock.tryLock();
                if (!hasLock){
                    System.out.println("没有获取到锁,放弃等待");
                    return;
                }
    
                try{
                    executeBusiness();
                }finally {
                    lock.unlock();
                }
            }).start();
        }
    
        private static void executeBusiness(){
            System.out.println("do business logic");
        }
    
        private static void sleep(int seconds){
            try {
                TimeUnit.SECONDS.sleep(seconds);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    • 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
    带超时
    • 等待过程中,如果获取到了锁,则执行正常的业务流程
    • 等待一段时间后,如果没有获取到锁,则中断获取锁的竞争
    • 等待过程中,依然可以被打断
    ReentrantLock        public boolean tryLock(long timeout, TimeUnit unit)
                                 throws InterruptedException
    
    • 1
    • 2
    package com.nike.erick.d03;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Demo06 {
    
        private static ReentrantLock reentrantLock = new ReentrantLock();
    
        public static void main(String[] args) throws InterruptedException {
    
            Thread firstThread = new Thread(() -> {
                reentrantLock.lock();
                try {
                    TimeUnit.SECONDS.sleep(2);
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    reentrantLock.unlock();
                }
            });
    
            Thread secondThread = new Thread(() -> {
                try {
                    /*最长等待3s*/
                    boolean hasLock = reentrantLock.tryLock(3, TimeUnit.SECONDS);
                    if (!hasLock) {
                        System.out.println("没有获取到锁");
                        return;
                    }
    
                    try {
                        System.out.println("Execution Business...");
                    } finally {
                        reentrantLock.unlock();
                    }
    
                } catch (InterruptedException e) {
                    System.out.println("等待锁过程中被打断了。。。");
                    e.printStackTrace();
                }
            });
    
            firstThread.start();
            TimeUnit.SECONDS.sleep(1);
            secondThread.start();
            secondThread.interrupt();
        }
    }
    
    • 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

    2. 条件变量

    • 多WaitSet, 可以和wait/notify更好的结合
    • ReentrantLock: 可以将等待的不同队列分类,然后根据队列来进行唤醒
    # 1. 创建一个等待的队列
    ReentrantLock    public Condition newCondition()
    
    # 2. 将一个线程在某个队列中进行等待
    Condition        public final void await() throws InterruptedException
    
    # 3. 去某个队列中唤醒等待的线程
    Condition        public final void signal()
                     public final void signalAll()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    package com.nike.erick.d03;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Demo07 {
    
        private static ReentrantLock reentrantLock = new ReentrantLock(true);
    
        private static Condition boyRoom = reentrantLock.newCondition();
        private static Condition girlRoom = reentrantLock.newCondition();
    
        private static boolean hasCigarette = false;
        private static boolean hasDinner = false;
    
        public static void main(String[] args) throws InterruptedException {
            /*抽烟线程*/
            for (int i = 0; i < 5; i++) {
                int boyNo = i;
                new Thread(() -> {
                    reentrantLock.lock();
                    while (true) {
                        if (!hasCigarette) {
                            try {
                                System.out.println("没有烟,女孩等会-" + boyNo);
                                boyRoom.await();// Condition的方法
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        } else {
                            break;
                        }
    
                        try {
                            System.out.println("男孩打仗-" + boyNo);
                        } finally {
                            reentrantLock.unlock();
                        }
                    }
                }).start();
            }
    
            for (int i = 0; i < 5; i++) {
                int girlNo = i;
                new Thread(() -> {
                    reentrantLock.lock();
                    while (true) {
                        if (!hasDinner) {
                            try {
                                System.out.println("没有外卖,女孩等会-" + girlNo);
                                girlRoom.await();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        } else {
                            break;
                        }
                    }
                    try {
                        System.out.println("女孩做鞋-" + girlNo);
                    } finally {
                        reentrantLock.unlock();
                    }
                }).start();
            }
    
            TimeUnit.SECONDS.sleep(1);
            new Thread(() -> {
                reentrantLock.lock();
                try {
                    hasCigarette = true;
                    boyRoom.signalAll(); // 唤醒,让男孩线程准备获取锁资源
                } finally {
                    reentrantLock.unlock();
                }
            }).start();
    
            TimeUnit.SECONDS.sleep(1);
    
            new Thread(() -> {
                reentrantLock.lock();
                try {
                    hasDinner = true;
                    girlRoom.signalAll(); // 唤醒,让女孩线程准备获取锁资源
                } finally {
                    reentrantLock.unlock();
                }
            }).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
    • 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
    • 90
    • 91

    3. ReentrantLock vs Synchronized

    可重入性:   Synchronized 和 ReentrantLock都支持
    可打断性:   Synchronized锁不能被打断                 ReentrantLock可以被打断,防止死锁
    超时性:     Synchronized锁获取时候会一直等待          ReentrantLock支持超时等待
    公平性:     Synchronized的EntryList是不公平         ReentrantLock(true)公平锁
    条件变量:   Synchronized的WaitSet只有一个            ReentrantLock支持不同的WaitSet
    
    • 1
    • 2
    • 3
    • 4
    • 5

    固定顺序输出

    1. 先2后1

    • 线程2运行完毕后,线程1再开始运行

    1.1 synchronized + wait + notify

    package com.erick.multithread.d3;
    
    public class Demo01 {
        private static Object lock = new Object();
    
        public static void main(String[] args) {
            new Thread(() -> {
                synchronized (lock) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
    
                    System.out.println("first-thread running");
                }
            }).start();
    
            new Thread(() -> {
                synchronized (lock) {
                    System.out.println("second-thread running");
                    lock.notifyAll();
                }
            }).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

    1.2 reentrylock + await + signal

    package com.erick.multithread.d3;
    
    import java.sql.Time;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Demo02 {
        private static ReentrantLock lock = new ReentrantLock();
    
        private static Condition room = lock.newCondition();
    
        public static void main(String[] args) throws InterruptedException {
            new Thread(() -> {
                try {
                    lock.lock();
                    room.await();
                    System.out.println("线程一执行完毕");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    lock.unlock();
                }
            }).start();
    
            TimeUnit.SECONDS.sleep(1);
    
            new Thread(() -> {
                try{
                    lock.lock();
                    System.out.println("线程二执行完毕");
                    room.signal();
                }finally {
                    lock.unlock();
                }
            }).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

    1.3 park + unpark

    package com.erick.multithread.d3;
    
    import java.util.concurrent.locks.LockSupport;
    
    public class Demo03 {
        public static void main(String[] args) {
            Thread firstThread = new Thread(() -> {
                LockSupport.park();
                System.out.println("线程一运行完毕");
            });
    
            firstThread.start();
    
            new Thread(() -> {
                System.out.println("线程二运行完毕");
                LockSupport.unpark(firstThread);
            }).start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2. 交替输出

    • 五个线程交替输出abcde

    2.1 synchronized + wait + notify

    package com.erick.multithread.d3;
    
    import java.util.Objects;
    
    public class Demo04 {
        private static Object lock = new Object();
    
        private static String baseChar = "a";
    
        public static void main(String[] args) {
            new Thread(() -> printCharacter("a", "b")).start();
            new Thread(() -> printCharacter("b", "c")).start();
            new Thread(() -> printCharacter("c", "d")).start();
            new Thread(() -> printCharacter("d", "e")).start();
            new Thread(() -> printCharacter("e", "a")).start();
        }
    
        private static void printCharacter(String srcChar, String targetChar) {
            while (true) {
                synchronized (lock) {
                    while (!Objects.equals(baseChar, srcChar)) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + baseChar);
                    baseChar = targetChar;
                    lock.notifyAll();
                }
            }
        }
    }
    
    • 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

    2.2 reentrylock + await + signal

    package com.erick.multithread.d3;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Demo05 {
        private static String baseChar = "a";
        private static ReentrantLock lock = new ReentrantLock();
        private static Condition firstRoom = lock.newCondition();
        private static Condition secondRoom = lock.newCondition();
        private static Condition thirdRoom = lock.newCondition();
        private static Condition fourthRoom = lock.newCondition();
        private static Condition fifthRoom = lock.newCondition();
    
        public static void main(String[] args) {
            new Thread(() -> printCharacter("a", "b", firstRoom, secondRoom)).start();
            new Thread(() -> printCharacter("b", "c", secondRoom, thirdRoom)).start();
            new Thread(() -> printCharacter("c", "d", thirdRoom, fourthRoom)).start();
            new Thread(() -> printCharacter("d", "e", fourthRoom, fifthRoom)).start();
            new Thread(() -> printCharacter("e", "a", fifthRoom, firstRoom)).start();
        }
    
        private static void printCharacter(String printChar, String targetChar, Condition waitRoom, Condition signalRoom) {
            /*利用不同的condition,不用考虑虚假唤醒问题*/
            while (true) {
                try {
                    lock.lock();
                    if (baseChar != printChar) {
                        waitRoom.await();
                    }
    
                    System.out.println(Thread.currentThread().getName() + ":" + printChar);
                    baseChar = targetChar;
                    signalRoom.signal();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } 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

    2.3 park + unpark

    package com.erick.multithread.d3;
    
    import java.util.concurrent.locks.LockSupport;
    
    public class Demo06 {
        private static String baseChar = "a";
        private static Thread firstThread;
        private static Thread secondThread;
        private static Thread thirdThread;
        private static Thread fourthThread;
        private static Thread fifthThread;
    
        public static void main(String[] args) {
            firstThread = new Thread(() -> printCharacter("a", "b", secondThread));
            secondThread = new Thread(() -> printCharacter("b", "c", thirdThread));
            thirdThread = new Thread(() -> printCharacter("c", "d", fourthThread));
            fourthThread = new Thread(() -> printCharacter("d", "e", fifthThread));
            fifthThread = new Thread(() -> printCharacter("e", "a", firstThread));
    
            firstThread.start();
            secondThread.start();
            thirdThread.start();
            fourthThread.start();
            fifthThread.start();
        }
    
        private static void printCharacter(String printChar, String targetChar, Thread nextThread) {
            while (true){
                if (baseChar != printChar) {
                    LockSupport.park();
                }
                System.out.println(Thread.currentThread().getName() + ":" + printChar);
                baseChar = targetChar;
                LockSupport.unpark(nextThread);
            }
        }
    }
    
    • 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

    3. 交替输出奇偶数

    • 两个线程,交替输出奇偶数

    3.1 synchronized + wait + notify

    package com.erick.multithread.d3;
    
    public class Demo07 {
    
        private static Object lock = new Object();
        private static int number = 0;
    
        public static void main(String[] args) {
            new Thread(() -> printNum(), "t1").start();
            new Thread(() -> printNum(), "t2").start();
        }
    
        private static void printNum() {
            for (int i = 0; i < 10; i++) {
                synchronized (lock) {
                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;
                    lock.notify();
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
    
    • 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

    3.2 reentrylock + await + signal

    package com.erick.multithread.d3;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Demo08 {
        private static int number = 0;
        private static ReentrantLock lock = new ReentrantLock();
        private static Condition room = lock.newCondition();
    
        public static void main(String[] args) {
            new Thread(() -> printNum(), "t1").start();
            new Thread(() -> printNum(), "t2").start();
        }
    
        private static void printNum() {
            for (int i = 0; i < 10; i++) {
                try {
                    lock.lock();
                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;
                    room.signal();
                    try {
                        room.await();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                } 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

    3.3 park + unpark

    package com.erick.multithread.d3;
    
    import java.util.concurrent.locks.LockSupport;
    
    public class Demo09 {
        private static int number = 0;
    
        private static Thread firstThread;
        private static Thread secondThread;
    
        public static void main(String[] args) {
            firstThread = new Thread(() -> printNum(secondThread));
            secondThread = new Thread(() -> printNum(firstThread));
    
            firstThread.start();
            secondThread.start();
    
            // 触发
            LockSupport.unpark(firstThread);
        }
    
        private static void printNum(Thread nextThread) {
            for (int i = 0; i < 10; i++) {
                LockSupport.park();
                System.out.println(Thread.currentThread().getName() + ":" + number);
                number++;
                LockSupport.unpark(nextThread);
            }
        }
    }
    
    • 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
  • 相关阅读:
    Go语言学习笔记—golang操作MySQL数据库
    PWMADC重要参数
    SpringCloud之Hystrix
    首个集成AI的国产操作系统带来了哪些惊喜
    DDD技术方案落地实践 | 京东云技术团队
    CVE-2023-23752:Joomla未授权访问漏洞复现
    浅谈OAuth 2.0与JWT
    “链圈”十年反思
    负载均衡技术全景:理论、实践与案例研究
    ShareSDK for Unity
  • 原文地址:https://blog.csdn.net/weixin_43374578/article/details/127945671