• JUC系列(一&二):线程基础、生产与消费模型、8锁问题


    发现 学习狂神JUC系列少了开篇 特此补发

    线程与进程

    线程、进程、如何来解释

    进程 : 一个程序 如 QQ.exe Music.exe 程序的集合

    一个进程可以包含多个线程,至少包含一个线程

    Java 默认是开启两个线程 main GC

    线程: 开了一个进程 比如: typora 写字,自动保存(线程负责的)

    对于Java 而言创建线程我们学习到的方法有三种 : Thread , Runnable , Callable

    PS :Java本身是无法开启线程的!!!

        public synchronized void start() {
            /**
             * This method is not invoked for the main method thread or "system"
             * group threads created/set up by the VM. Any new functionality added
             * to this method in the future may have to also be added to the VM.
             *
             * A zero status value corresponds to state "NEW".
             */
            if (threadStatus != 0)
                throw new IllegalThreadStateException();
    
            /* Notify the group that this thread is about to be started
             * so that it can be added to the group's list of threads
             * and the group's unstarted count can be decremented. */
            group.add(this);
    
            boolean started = false;
            try {
                start0();
                started = true;
            } finally {
                try {
                    if (!started) {
                        group.threadStartFailed(this);
                    }
                } catch (Throwable ignore) {
                    /* do nothing. If start0 threw a Throwable then
                      it will be passed up the call stack */
                }
            }
        }
    
    	// 本地方法 调用底层的 C++ Java本身不能直接操作硬件
        private native void start0();
    
    • 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

    并发,并行

    我们要学习并发编程,首先要了解 : 并发和并行是什么

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

    • CPU一核,模拟出来多条线程,天下武功,唯快不破,快速交替。

    并行(多个程序一起走)

    • CPU 多核,多个线程同时执行;如果需要提高性能 : 线程池

    查看一下自己的cpu核数

    image-20220301201453940

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

    所有的公司都很看重,就是效率,比如裁员:一个厉害的可以大于两个不再那么厉害的甚至更多

    比如: 做事情的速度 高手:1s,一般人: 10s 肯定是 1s 的更加的之前也更加的有效率;

    做到人员(减少),但是技术力却提高了

    线程有几个状态

    通过Thread 源码里的枚举类:State 中的属性可以看出来

    线程有六个状态

        public e
    num State {
            //新的线程
            NEW,
            //运行中
            RUNNABLE,
            // 阻塞
            BLOCKED,
            //等待,一直等
            WAITING,
            //等待一段时间就不等了
            TIMED_WAITING,
            // 终止线程
            TERMINATED;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    wait/sleep的区别

    1、他们来自不同的类

    wait => object

    sleep => Thread

    2、关于锁的释放

    wait会释放锁,sleep不会释放锁

    抽象理解 : sleep 睡着了没办法释放, wait 是等待,有人需要的释放

    wait必须在同步代码块中使用,得有需要等的人

    sleep可以在任何地方使用

    3、是否需要捕获异常

    wait 不需要捕获异常

    sleep 需要捕获异常

    Lock锁

    传统 Synchronized

    传统火车票案例

    /**
     * @projectName: JUC
     * @package: Thread
     * @className: SaleTicketDemo
     * @author: 冷环渊 doomwatcher
     * @description: TODO
     * @date: 2022/3/1 20:28
     * @version: 1.0
     */
    public class SaleTicketDemo {
        /**
         * 真正的多线程开发 公司中的需要降低耦合性
         * 线程就是一个单独的资源类,没有任何附属的操作
         * 1、 属性。方法
         * */
        public static void main(String[] args) {
            //并发: 多线程操作同一个资源类,把资源丢入线程
            Ticket ticket = new Ticket();
            new Thread(() -> {
                for (int i = 0; i < 40; i++) {
                    ticket.sale();
                }
            }, "A").start();
            new Thread(() -> {
                for (int i = 0; i < 40; i++) {
                    ticket.sale();
                }
            }, "B").start();
            new Thread(() -> {
                for (int i = 0; i < 40; i++) {
                    ticket.sale();
                }
            }, "C").start();
        }
    }
    
    //资源类 OOP
    class Ticket {
        //属性 方法
        private int number = 50;
    
        //    买票的方式
        public synchronized void sale() {
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余" + number);
            }
        }
    }
    
    
    • 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

    Lock接口

    image-20220301204107904

    常用的一些实现类

    image-20220301204151242

    image-20220301204430613

    公平锁 : 十分公平,可以先来后到 比如 前一个线程要执行30s 后面的 需要3s 后者必须等待前者执行完

    非公平锁 : 十分不公平,可以根据条件来插队

    如何用Lock来编写火车票Demo

    1. new ReentrantLock();
    2. Lock.lock(); 解锁
    3. finally => lock.unlock(); 解锁

    Synchronized和Lock区别

    1. Synchronized 内置的java关键字,Lock是一个java类
    2. Synchronized 无法获取锁的状态 Lock 可以判断是否获取到锁
    3. Synchronized 会自动释放锁, Lock 必须手动解锁,如果不释放锁 就会死锁
    4. Synchronized 线程1(获得锁,阻塞),线程2(等待,死等),Lock锁不一定会等待
    5. Synchronized 可重入锁,不可以中断,非公平,Lock 可重入锁,可以判断锁,手动调整
    6. Synchronized 适合锁少量代码同步问题,Lock 适合锁大量的同步代码

    锁是什么,如何判断锁的是谁

    生产者和消费者问题

    生产者和消费者问题:Synchronized 版

    面试高频 : 单例模式,排序算法,生产者和消费者 死锁

    package ProduceAndconsum;
    
    /**
     * @projectName: JUC
     * @package: ProduceAndconsum
     * @className: A
     * @author: 冷环渊 doomwatcher
     * @description: TODO
     * @date: 2022/3/1 21:08
     * @version: 1.0
     */
    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.decrement();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "B").start();
        }
    }
    
    //数字 资源类
    // 1、 判断等待,通知
    class Data {
        private int number = 0;
    
        //    +1
        public synchronized void increment() throws InterruptedException {
            if (number != 0) {
                //    等待
                this.wait();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            //    通知其他线程 +1 完毕了
            this.notifyAll();
        }
    
        //    -1
        public synchronized void decrement() throws InterruptedException {
            if (number == 0) {
                //    等待
                this.wait();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            //    通知其他线程 -1 完毕了
            this.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
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    但是,写出来这个简单的模型 面试官还是会挑的出毛病来,问题出在那?

    问题,现在我们是两个线程 ,我们加到四个线程 A,B,C,D 现在还是安全的吗?答案是肯定的不是

    虚假唤醒问题:我们增加到四个线程输出的时候就会发现一些问题来,输出不再是0101了

    image-20220301212425229

    image-20220301212149214

    这里如何解决呢? 将 if 换成 while循环

    修改之后根据官方文档的解释之后,将if改编成while

    image-20220301212605922

    输出就又回到了正常

    package ProduceAndconsum;
    
    /**
     * @projectName: JUC
     * @package: ProduceAndconsum
     * @className: A
     * @author: 冷环渊 doomwatcher
     * @description: TODO
     * @date: 2022/3/1 21:08
     * @version: 1.0
     */
    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.decrement();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "B").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();
                    }
                }
            }, "D").start();
        }
    }
    
    //数字 资源类
    // 1、 判断等待,通知
    class Data {
        private int number = 0;
    
        //    +1
        public synchronized void increment() throws InterruptedException {
            while (number != 0) {
                //    等待
                this.wait();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            //    通知其他线程 +1 完毕了
            this.notifyAll();
        }
    
        //    -1
        public synchronized void decrement() throws InterruptedException {
            while (number == 0) {
                //    等待
                this.wait();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            //    通知其他线程 -1 完毕了
            this.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
    • 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

    生产者和消费者问题: JUC

    在新的学习中 synchronized 有 Lock 替换

    那么 wait 和 notify谁来替换呢?

    image-20220301212829262

    通过 Lock来找到 Condition

    image-20220301213020385

    image-20220301213059259

    package ProduceAndconsum;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @projectName: JUC
     * @package: ProduceAndconsum
     * @className: B
     * @author: 冷环渊 doomwatcher
     * @description: TODO
     * @date: 2022/3/1 21:31
     * @version: 1.0
     */
    
    
    public class B {
        public static void main(String[] args) {
            Data2 data = new Data2();
            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.decrement();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "B").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();
                    }
                }
            }, "D").start();
        }
    }
    
    //数字 资源类
    // 1、 判断等待,通知
    class Data2 {
        private int number = 0;
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        //等待
        //    condition.await();
        //唤醒全部
        //    condition.signalAll();
    
        //    +1
        public void increment() throws InterruptedException {
            lock.lock();
            try {
                while (number != 0) {
                    //    等待
                    condition.await();
                }
                number++;
                System.out.println(Thread.currentThread().getName() + "=>" + number);
                //    通知其他线程 +1 完毕了
                condition.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        //    -1
        public void decrement() throws InterruptedException {
            lock.lock();
            try {
                while (number == 0) {
                    //    等待
                    condition.await();
                }
                number--;
                System.out.println(Thread.currentThread().getName() + "=>" + number);
                //    通知其他线程 -1 完毕了
                condition.signalAll();
            } catch (InterruptedException 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
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    package ProduceAndconsum;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @projectName: JUC
     * @package: ProduceAndconsum
     * @className: B
     * @author: 冷环渊 doomwatcher
     * @description: TODO
     * @date: 2022/3/1 21:31
     * @version: 1.0
     */
    
    
    public class B {
        public static void main(String[] args) {
            Data2 data = new Data2();
            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.decrement();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "B").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();
                    }
                }
            }, "D").start();
        }
    }
    
    //数字 资源类
    // 1、 判断等待,通知
    class Data2 {
        private int number = 0;
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        //等待
        //    condition.await();
        //唤醒全部
        //    condition.signalAll();
    
        //    +1
        public void increment() throws InterruptedException {
            lock.lock();
            try {
                while (number != 0) {
                    //    等待
                    condition.await();
                }
                number++;
                System.out.println(Thread.currentThread().getName() + "=>" + number);
                //    通知其他线程 +1 完毕了
                condition.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        //    -1
        public void decrement() throws InterruptedException {
            lock.lock();
            try {
                while (number == 0) {
                    //    等待
                    condition.await();
                }
                number--;
                System.out.println(Thread.currentThread().getName() + "=>" + number);
                //    通知其他线程 -1 完毕了
                condition.signalAll();
            } catch (InterruptedException 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
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108

    任何的一个新技术,都不可能是单纯的去替代老的技术,一定有优化和补充

    Condition

    image-20220301214146773

    我们更换完代码之后,可以正常输出但是还是混乱的,我们想要达到 A->B->C->D 这样输出,这个时候就引出了新的知识点

    Conddition精准唤醒

    package ProduceAndconsum;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @projectName: JUC
     * @package: ProduceAndconsum
     * @className: B
     * @author: 冷环渊 doomwatcher
     * @description: TODO
     * @date: 2022/3/1 21:31
     * @version: 1.0
     */
    
    
    public class C {
        public static void main(String[] args) {
            Data3 data = new Data3();
            new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    data.printA();
                }
            }, "A").start();
            new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    data.printB();
                }
            }, "B").start();
            new Thread(() -> {
                for (int i = 0; i < 10; i++) {
                    data.printC();
                }
            }, "C").start();
        }
    }
    
    //数字 资源类
    // 1、 判断等待,通知
    class Data3 {
        private int number = 1;
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        Condition condition1 = lock.newCondition();
        Condition condition2 = lock.newCondition();
    
        public void printA() {
            lock.lock();
            try {
                //    业务,判断 执行 通知
                while (number != 1) {
                    //等待
                    condition.await();
                }
                System.out.println(Thread.currentThread().getName() + "=>AAA");
                number = 2;
                condition1.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void printB() {
            lock.lock();
            try {
                //    业务,判断 执行 通知
                while (number != 2) {
                    //等待
                    condition1.await();
                }
                System.out.println(Thread.currentThread().getName() + "=>BBB");
                number = 3;
                condition2.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void printC() {
            lock.lock();
            try {
                //    业务,判断 执行 通知
                while (number != 3) {
                    //等待
                    condition2.await();
                }
                System.out.println(Thread.currentThread().getName() + "=>BBB");
                number = 1;
                condition.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
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101

    输出的结果就达到我们的预期了

    image-20220301215538935

    8锁现象

    锁 ----> 刚new 出来的对象、class

    8锁就是关于的锁的八个问题,下面也有四个demo类来阐述各种场景下锁的不同状态

    demo1

    两个问题:

    正常模式下 在两个同步线程方法调用的时候 中途延时1s 会不会改变输出结果

    答:不会影响输出顺序

    正常模式下 同步方法内延时4s 会不会印象输出

    答:不会影响输出顺序

    • 标准情况下 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
    • 发短信延时四秒 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话

    demo代码

    package lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * @projectName: JUC
     * @package: Lock8
     * @className: lock8Demo
     * @author: 冷环渊 doomwatcher
     * @description: TODO
     *  Lock8 就是关于锁的八个问题
     * 1. 标准情况下 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
     * 2. 发短信延时四秒 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
     * @date: 2022/3/2 1:13
     * @version: 1.0
     */
    public class lock8Demo {
        public static void main(String[] args) {
            phone phone = new phone();
            new Thread(() -> {
                phone.sendSms();
            }, "A").start();
            //    捕获
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(() -> {
                phone.call();
            }, "B").start();
        }
    }
    
    class phone {
        //synchronized 锁的对象是方法调用者,
        //两个方法用的都是 phone 对象的锁,两个方法谁先拿到锁 谁执行
        public synchronized void sendSms() {
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("发短信");
        }
    
        public synchronized void call() {
            System.out.println("打电话");
        }
    }
    
    • 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

    demo2

    同步方法执行和普通方法执行顺序

    答 :普通方法没有锁,所以普通方法先

    两个对象执行会不会影响顺序

    答:会 两个不同的对象锁也是不同的 ,对象1 还在等待,对象2调用的call方法不用等待所以先输出

    • 现在新增一个普通方法 问 先发短信还是先发hello 先输出hello
    • 新增两个对象 是先打电话还是先发短信
    package lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * @projectName: JUC
     * @package: Lock8
     * @className: lock8Demo
     * @author: 冷环渊 doomwatcher
     * @description: TODO
     *  Lock8 就是关于锁的八个问题
     * 3、 现在新增一个普通方法  问 先发短信还是先发hello 先输出hello
     * 4、 新增两个对象 是先打电话还是先发短信
     * @date: 2022/3/2 1:13
     * @version: 1.0
     */
    public class lock8Demo2 {
        public static void main(String[] args) {
            //两个对象,现在是两个调用者所以是两个锁
            phone2 phone = new phone2();
            phone2 phone1 = new phone2();
            new Thread(() -> {
                phone.sendSms();
            }, "A").start();
            //    捕获
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(() -> {
                phone1.call();
            }, "B").start();
        }
    }
    
    class phone2 {
        //synchronized 锁的对象是方法调用者,
        //两个方法用的都是 phone 对象的锁,两个方法谁先拿到锁 谁执行
        public 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

    demo3

    一个对象调用静态同步方法会不会改动执行顺序

    答: 不会 ,static是锁的类模版全局唯一,不会改变锁的执行交换顺序

    两个对象调用静态同步方法会不会改变执行顺序

    答:不会 static修饰的是类模版,锁的也是类模板而不是类对象,只要是这个类生成的对象,不管多少个都不会改变顺序

    • 一个对象 添加两个静态的同步方法,只有一个对象 先打印 发短信 还是打电话?
    • 添加两个对象,分别调用静态同步方法 先打印 发短信 还是打电话?
    package lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * @projectName: JUC
     * @package: Lock8
     * @className: lock8Demo
     * @author: 冷环渊 doomwatcher
     * @description: TODO
     *  Lock8 就是关于锁的八个问题
     *  5、添加两个静态的同步方法,只有一个对象 先打印 发短信 还是打电话?
     *  6、 添加两个对象,增加两个同步方法 先打印  发短信 还是打电话?
     * @date: 2022/3/2 1:13
     * @version: 1.0
     */
    public class lock8Demo3 {
        public static void main(String[] args) {
    
            //不管多少对象, 使用的都是底层的唯一class所以不管怎么怎么改变结果都不会变,
            // 因为synchroized锁的是class对象,static修饰的方法 类一开始就加载了,
            new Thread(() -> {
                Phone3.sendSms();
            }, "A").start();
            //    捕获
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(() -> {
                Phone3.call();
            }, "B").start();
        }
    }
    
    //phone3是唯一的一个class
    class Phone3 {
        //synchronized 锁的对象是方法调用者,
        //两个方法用的都是 phone 对象的锁,两个方法谁先拿到锁 谁执行
        // 这里 static 是静态方法 ,类一加载就有了,这个用的锁不再是 phone锁 而是class锁
        public static synchronized void sendSms() {
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("发短信");
        }
    
        public static synchronized void call() {
            System.out.println("打电话");
        }
    
    }
    
    • 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

    demo4

    • 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,因为锁的不是一个东西,sync锁的是类,static 是锁class
    • 两个对象 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,

    答: 不是同一个锁,谁执行快就输出谁,

    package lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * @projectName: JUC
     * @package: Lock8
     * @className: lock8Demo
     * @author: 冷环渊 doomwatcher
     * @description: TODO
     *  Lock8 就是关于锁的八个问题
     *  7、一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,因为锁的不是一个东西,sync锁的是类,static 是锁class
     *  8、两个对象 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,
     * @date: 2022/3/2 1:13
     * @version: 1.0
     */
    public class lock8Demo4 {
        public static void main(String[] args) {
            Phone4 phone = new Phone4();
            //不管多少对象, 使用的都是底层的唯一class所以不管怎么怎么改变结果都不会变,
            // 因为synchroized锁的是class对象,static修饰的方法 类一开始就加载了,
            new Thread(() -> {
                Phone4.sendSms();
            }, "A").start();
            //    捕获
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(() -> {
                phone.call();
            }, "B").start();
        }
    }
    
    //phone3是唯一的一个class
    class Phone4 {
        //静态同步方法是锁的 class
        public static synchronized void sendSms() {
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("发短信");
        }
    
        //这里普通同步方法锁的是 phone
        public synchronized void call() {
            System.out.println("打电话");
        }
    
    }
    
    • 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

    小结

    我们通过八种不同的情况来理解锁在不同情况下的执行情况:

    • 标准情况下 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
    • 发短信延时四秒 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
    • 现在新增一个普通方法 问 先发短信还是先发hello 先输出hello
    • 新增两个对象 是先打电话还是先发短信
    • 一个对象 添加两个静态的同步方法,只有一个对象 先打印 发短信 还是打电话?
    • 添加两个对象,分别调用静态同步方法 先打印 发短信 还是打电话?
    • 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,因为锁的不是一个东西,sync锁的是类,static 是锁class
    • 两个对象 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,

    大家可以自己写一下代码看一些这些问题的结果,实践出效果,实践出理解

    锁的东西,无非就是对象和 类模版

    1. new 出来的对象 就是锁具体的对象,比如普通同步方法
    2. 带有static 修饰的静态同步方法 锁的是类模版是全局唯一的对象class如 : class
  • 相关阅读:
    数据结构与算法拾遗十(一些基本的数据结构)
    爬虫案例:建设库JS逆向
    1225. 报告系统状态的连续日期
    数据转换为excel模板下载
    一文解读 NFT 零版税
    NPM配置国内镜像源
    ubuntu修改用户名和用户密码
    二叉树题目:最大二叉树 II
    ctfshow 命令执行(40-50)
    uniapp 地图跳转到第三方导航软件 直接打包成apk
  • 原文地址:https://blog.csdn.net/doomwatcher/article/details/133365615