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

并发,并行

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

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

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

并行(多个程序一起走)

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

查看一下自己的cpu核数

JUC系列(一&二):线程基础、生产与消费模型、8锁问题_第1张图片

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

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

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

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

线程有几个状态

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

线程有六个状态

    public e
num State {
        //新的线程
        NEW,
        //运行中
        RUNNABLE,
        // 阻塞
        BLOCKED,
        //等待,一直等
        WAITING,
        //等待一段时间就不等了
        TIMED_WAITING,
        // 终止线程
        TERMINATED;
    }

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

Lock接口

JUC系列(一&二):线程基础、生产与消费模型、8锁问题_第2张图片

常用的一些实现类

JUC系列(一&二):线程基础、生产与消费模型、8锁问题_第3张图片

JUC系列(一&二):线程基础、生产与消费模型、8锁问题_第4张图片

公平锁 : 十分公平,可以先来后到 比如 前一个线程要执行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();
    }
}

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

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

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

JUC系列(一&二):线程基础、生产与消费模型、8锁问题_第5张图片

JUC系列(一&二):线程基础、生产与消费模型、8锁问题_第6张图片

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

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

JUC系列(一&二):线程基础、生产与消费模型、8锁问题_第7张图片

输出就又回到了正常

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

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

在新的学习中 synchronized 有 Lock 替换

那么 wait 和 notify谁来替换呢?

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

通过 Lock来找到 Condition

JUC系列(一&二):线程基础、生产与消费模型、8锁问题_第9张图片

JUC系列(一&二):线程基础、生产与消费模型、8锁问题_第10张图片

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

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

Condition

JUC系列(一&二):线程基础、生产与消费模型、8锁问题_第11张图片

我们更换完代码之后,可以正常输出但是还是混乱的,我们想要达到 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();
        }
    }
}

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

JUC系列(一&二):线程基础、生产与消费模型、8锁问题_第12张图片

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("打电话");
    }
}

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");
    }
}

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("打电话");
    }

}

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

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

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

  1. new 出来的对象 就是锁具体的对象,比如普通同步方法
  2. 带有static 修饰的静态同步方法 锁的是类模版是全局唯一的对象class如 : class

你可能感兴趣的:(JUC,开发语言,java,juc,高并发,多线程)