1 线程间通信
线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的。我们来基本一道面试常见的题目来分析
场景:两个线程,一个线程对当前数值加 1,另一个线程对当前数值减 1,要求用线程间通信
1.1 synchronized方法
// 第一步 创建资源类,定义属性和操作方法
class Share{
    //初始值
    private int number = 0;
    // +1的方法
    public synchronized void incr() throws InterruptedException {
        // 判断
        while(number != 0) {
            this.wait();
        }
        // 干活
        number++;
        System.out.println(Thread.currentThread().getName() + " :: " + number);
        //通知其他线程
        this.notifyAll();
    }
    // -1的方法
    public synchronized void decr() throws InterruptedException {
        // 判断
        while(number != 1) {
            this.wait();
        }
        // 干活
        number--;
        System.out.println(Thread.currentThread().getName() + " :: " + number);
        //通知其他线程
        this.notifyAll();
    }
}
public class ThreadDemo1 {
    // 第三步创建多个线程,调用资源类的操作方法
    public static void main(String[] args) {
        Share share = new Share();
        // 创建线程
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();//+1
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"AA").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr(); //-1
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"BB").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();//+1
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"CC").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr(); //-1
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"DD").start();
    }
}
1.2 Lock方案
// 第一步 创建资源类,定义属性和操作方法
class Share{
    //初始值
    private int number = 0;
    //创建Lock
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    // +1
    public void incr() throws InterruptedException {
        //上锁
        lock.lock();
        try {
            //判断
            while (number != 0){
                condition.await();
            }
            //干活
            number++;
            System.out.println(Thread.currentThread().getName() + " :: " + number);
            //通知
            condition.signalAll();
        }finally {
            // 解锁
            lock.unlock();
        }
    }
    // -1
    public void decr() throws InterruptedException {
        //上锁
        lock.lock();
        try {
            //判断
            while (number != 1){
                condition.await();
            }
            //干活
            number--;
            System.out.println(Thread.currentThread().getName() + " :: " + number);
            //通知
            condition.signalAll();
        }finally {
            // 解锁
            lock.unlock();
        }
    }
}
public class ThreadDemo2 {
    // 第三步创建多个线程,调用资源类的操作方法
    public static void main(String[] args) {
        Share share = new Share();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"AA").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"BB").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"CC").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"DD").start();
    }
}
1.3 虚假唤醒问题
在Java中,线程间通信通常使用 Object 类的 wait()、notify() 和 notifyAll() 方法来实现。这些方法与条件变量类似,但同样可能会出现虚假唤醒(Spurious Wakeup)的问题。
1.3.1 什么是虚假唤醒?
虚假唤醒是指一个线程在没有被显式通知的情况下被唤醒。换句话说,即使没有调用 notify() 或 notifyAll() 方法,等待的线程也可能被唤醒。这种现象在某些操作系统或线程库中是允许的,因为它可以简化某些实现。
1.3.2 为什么会出现虚假唤醒?
虚假唤醒的原因可能包括:
- 操作系统调度:操作系统可能在某些情况下唤醒线程,即使没有显式的通知。
- 多核处理器:在多核处理器上,线程可能在不同的核心上运行,导致某些同步机制不完全可靠。
- Java 实现:Java 的线程库实现可能会允许虚假唤醒,以提高性能或简化实现。
1.3.3 如何处理虚假唤醒?
为了避免虚假唤醒带来的问题,通常的做法是在循环中检查条件变量。这样,即使线程被虚假唤醒,它也会在循环中重新检查条件,如果条件不满足,线程会继续等待。
以下是一个使用 wait() 和 notifyAll() 的典型示例:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SpuriousWakeupExample {
    private static final Lock lock = new ReentrantLock();
    private static final Condition condition = lock.newCondition();
    private static boolean ready = false;
    public static void main(String[] args) throws InterruptedException {
        Thread workerThread = new Thread(new Worker());
        workerThread.start();
        // 主线程设置条件并通知
        Thread.sleep(1000); // 模拟一些工作
        lock.lock();
        try {
            ready = true;
            condition.signalAll(); // 通知等待的线程
        } finally {
            lock.unlock();
        }
        workerThread.join();
    }
    static class Worker implements Runnable {
        @Override
        public void run() {
            lock.lock();
            try {
                // 在循环中检查条件
                while (!ready) {
                    condition.await(); // 等待条件满足
                }
                // 条件满足,执行工作
                System.out.println("Worker thread is processing data");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
            }
        }
    }
}
1.3.4 关键点
- 循环检查条件:在 await()方法周围使用while循环来检查条件,而不是if语句。这样可以确保即使线程被虚假唤醒,它也会重新检查条件。
- 使用 Lock和Condition:在示例中使用了ReentrantLock和Condition,这是 Java 中更灵活的同步机制。你也可以使用synchronized关键字和Object的wait()、notify()和notifyAll()方法,但原理是相同的。
- 处理中断:在 await()方法中捕获InterruptedException,并处理线程中断的情况。
通过这种方式,可以有效避免虚假唤醒带来的问题,确保线程在条件真正满足时才继续执行。
1.4 多线程编程步骤
-  第一步创建资源类,在资源类创建属性和操作方法 
-  第二步在资源类操作方法 - 判断
- 干活
- 通知
 
-  第三步创建多个线程,调用资源类的操作方法 
-  第四步防止虚假唤醒问题 
2 线程间定制化通信
让线程按照指定顺序进行通信。
2.1 案例介绍
启动三个线程,按照如下要求执行:
 AA线程打印 5 次 A,BB 线程打印 10 次 B,CC 线程打印 15 次 C,按照此顺序循环 10 轮
2.2 流程分析

2.3 代码实现
// 第一步 创建资源类,定义属性和操作方法
class ShareResourse{
    // 定义标识位
    private int flag = 1; // 1:AA; 2:BB; 3:CC
    //创建Lock
    private Lock lock = new ReentrantLock();
    //创建三个condition
    private Condition c1 = lock.newCondition();
    private Condition c2 = lock.newCondition();
    private Condition c3 = lock.newCondition();
    //打印5次,参数第几轮
    public void print5(int loop) throws InterruptedException {
        //上锁
        lock.lock();
        try{
            while (flag != 1){
                //等待
                c1.await();
            }
            //干活
            for (int i = 0; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName() + " :: " + i +", 轮数:"+ loop);
            }
            //通知
            flag = 2; //修改标识位2
            c2.signal();//通知BB线程
        }finally {
            // 释放锁
            lock.unlock();
        }
    }
    //打印10次,参数第几轮
    public void print10(int loop) throws InterruptedException {
        //上锁
        lock.lock();
        try{
            while (flag != 2){
                //等待
                c2.await();
            }
            //干活
            for (int i = 0; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + " :: " + i +", 轮数:"+ loop);
            }
            //通知
            flag = 3; //修改标识位3
            c3.signal();//通知CC线程
        }finally {
            // 释放锁
            lock.unlock();
        }
    }
    //打印15次,参数第几轮
    public void print15(int loop) throws InterruptedException {
        //上锁
        lock.lock();
        try{
            while (flag != 3){
                //等待
                c3.await();
            }
            //干活
            for (int i = 0; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName() + " :: " + i +", 轮数:"+ loop);
            }
            //通知
            flag = 1; //修改标识位1
            c1.signal();//通知AA线程
        }finally {
            // 释放锁
            lock.unlock();
        }
    }
}
public class ThreadDemo3 {
    // 第三步创建多个线程,调用资源类的操作方法
    public static void main(String[] args) {
        ShareResourse shareResourse = new ShareResourse();
        new Thread(()->{
            for (int i = 0; i <= 10; i++) {
                try {
                    shareResourse.print5(i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"AA").start();
        new Thread(()->{
            for (int i = 0; i <= 10; i++) {
                try {
                    shareResourse.print10(i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"BB").start();
        new Thread(()->{
            for (int i = 0; i <= 10; i++) {
                try {
                    shareResourse.print15(i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"CC").start();
    }
}
synchronized实现同步的基础:Java中的每一个对象都可以作为锁。具体表现为以下3种形式。
 对于普通同步方法,锁是当前实例对象。
 对于静态同步方法,锁是当前类的class对象。
 对于同步方法块,锁是synchonized括号里配置的对象
3 思维导图

4 参考链接
【【尚硅谷】大厂必备技术之JUC并发编程】






![[数据库实验一]数据库和表](https://i-blog.csdnimg.cn/direct/777e5f25bff14140970611a3db93f9f8.png)












