03-线程间通信

  1. 一、线程间通信
    1. 1. synchronized 方案
    2. 2. Lock 方案
  2. 二、线程间定制化通信
    1. 1. 案例介绍
    2. 2. 实现过程

一、线程间通信

线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的。我们来基本一道面试常见的题目来分析

场景—两个线程,一个线程对当前数值加 1,另一个线程对当前数值减 1,要求用线程间通信

1. synchronized 方案

class TestVolatile {
    public static void main(String[] args) {
        DemoClass demoClass = new DemoClass();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    demoClass.increment();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "线程A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    demoClass.decrement();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "线程B").start();
    }
}

public class DemoClass {
    private int number = 0;

    public synchronized void increment() throws InterruptedException {
        while (number != 0) {
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName() + "执行了加1操作,当前值为:" + number);
        notifyAll();
    }


    public synchronized void decrement() throws InterruptedException {
        while (number != 1) {
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "执行了减1操作,当前值为:" + number);
        notifyAll();
    }
}

2. Lock 方案

class ShareNum {
    private int number = 0;

    private ReentrantLock lock = new ReentrantLock(true);
    private Condition condition = lock.newCondition();

    // ++操作
    public void increase() {
        // 上锁
        lock.lock();
        try {
            while (number != 0) {
                condition.await();
            }
            // 执行 ++ 操作
            number++;
            System.out.println(Thread.currentThread().getName() + "执行了 ++ 操作,当前number值为:" + number);
            // 唤醒其它线程
            condition.signalAll();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            // 解锁
            lock.unlock();
        }
    }

    // -- 操作
    public void decrease() {
        // 上锁
        lock.lock();
        try {
            while (number != 1) {
                condition.await();
            }
            // 执行 -- 操作
            number--;
            System.out.println(Thread.currentThread().getName() + "执行了 -- 操作,当前number值为:" + number);
            // 唤醒其它线程
            condition.signalAll();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            // 解锁
            lock.unlock();
        }
    }
}

public class ConditionDemo {
    public static void main(String[] args) {
        ShareNum shareNum = new ShareNum();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                shareNum.increase();
            }
        }, "AA").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                shareNum.decrease();
            }
        }, "BB").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                shareNum.increase();
            }
        }, "CC").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                shareNum.decrease();
            }
        }, "DD").start();
    }
}

二、线程间定制化通信

1. 案例介绍

A 线程打印 5 次 A,B 线程打印 10 次 B,C 线程打印 15 次 C,按照此顺序循环 10 轮

2. 实现过程

public class PrintTest {

    // 线程通信标识:0-线程A  1-线程B  2-线程C
    private int number = 0;
    // 创建锁
    private Lock lock = new ReentrantLock();
    // 线程A的通信钥匙
    private Condition conditionA =lock.newCondition();
    // 线程B的通信钥匙
    private Condition conditionB =lock.newCondition();
    // 线程C的通信钥匙
    private Condition conditionC =lock.newCondition();

    /**
     * 打印五次A
     */
    public void printA(int round) {
        try {
            lock.lock();
            while (number != 0) {
                conditionA.await(); // 等待并释放锁
            }
            for (int i = 0; i < 5; i++) {
                System.out.println("第 " + round + "轮:" + Thread.currentThread().getName() + "执行中,开始打印:=== A ===");
            }
            // 打印B
            number = 1;
            conditionB.signal();
        } catch (Exception e) {

        } finally {
            lock.unlock();
        }
    }

    /**
     * 打印10次B
     */
    public void printB(int round) {
        try {
            lock.lock();
            while (number != 1) {
                conditionB.await(); // 等待并释放锁
            }
            for (int i = 0; i < 10; i++) {
                System.out.println("第 " + round + "轮:" + Thread.currentThread().getName() + "执行中,开始打印:=== B ===");
            }
            // 打印B
            number = 2;
            conditionC.signal();
        } catch (Exception e) {

        } finally {
            lock.unlock();
        }
    }

    /**
     * 打印15次C
     */
    public void printC(int round) {
        try {
            lock.lock();
            while (number != 2) {
                conditionC.await(); // 等待并释放锁
            }
            for (int i = 0; i < 15; i++) {
                System.out.println("第 " + round + "轮:" + Thread.currentThread().getName() + "执行中,开始打印:=== C ===");
            }
            // 打印B
            number = 0;
            conditionA.signal();
        } catch (Exception e) {

        } finally {
            lock.unlock();
        }
    }

}

class MainClass {
    public static void main(String[] args) {
        PrintTest printTest = new PrintTest();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                printTest.printA(i);
            }
        }, "线程A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                printTest.printB(i);
            }
        }, "线程B").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                printTest.printC(i);
            }
        }, "线程C").start();
    }
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 george_95@126.com