【Java】MultiThread 多线程 Re02 线程通讯

一、等待与唤醒

/**
 * 线程通讯问题
 * Object wait, notify, notifyAll
 * Condition await signal signAll
 * CountDownLatch 当前线程等待若干个其他线程执行完成之后再执行
 * CyclicBarrier 一组线程等待某个状态之后再全部开始执行
 * Semaphore 控制某一组资源的访问权限
 */

 

- 代码案例,奇数线程打印奇数,偶数线程打印偶数

1、使用Object自带的方法实现等待与唤醒:

  /**
     * 休眠唤醒案例
     * 打印10以内的奇偶数
     * 奇数线程打印,偶数线程等待
     *
     * 这个案例使用锁对象实现
     */
    static class OddAndEvenDemo {
        private int printNo = 0;
        private Object lockObj = new Object();
        /**
         * 奇数打印方法,由奇数线程调用
         */
        public void odd() {
            while (printNo < 10) {
                synchronized (lockObj) {
                    if (!isEvenNo(printNo)) {
                        System.out.println("奇数:" + printNo);
                        printNo ++;
                        lockObj.notify();
                    } else {
                        try {
                            lockObj.wait(); // 等待偶数线程执行完毕
                        } catch (Exception exception) {
                            exception.printStackTrace();
                        }
                    }
                }
            }
        }

        /**
         * 偶数打印方法,偶数线程调用
         */
        public void even() {
            while (printNo < 10) {
                synchronized (lockObj) {
                    if (isEvenNo(printNo)) {
                        System.out.println("偶数:" + printNo);
                        printNo ++;
                        lockObj.notify();
                    } else {
                        try {
                            lockObj.wait(); // 等待偶数线程执行完毕
                        } catch (Exception exception) {
                            exception.printStackTrace();
                        }
                    }
                }
            }
        }
    }

判断是否奇偶数的方法:

    static boolean isEvenNo(int evenNo) {
        return evenNo % 2 == 0;
    }

执行部分:

    /**
     * 使用锁对象自身的等待与唤醒方法实现
     */
    @Test
    public void useLockObj() {
        OddAndEvenDemo oddAndEvenDemo = new OddAndEvenDemo();

        // 开启奇数线程
        Thread oddThread = new Thread(() -> oddAndEvenDemo.odd());
        // 开启偶数线程
        Thread evenThread = new Thread(() -> oddAndEvenDemo.even());
        oddThread.start();
        evenThread.start();
    }

 

2、使用Condition实现等待与唤醒:

  /**
     *  等待唤醒Condition方法
     */
    static class OddAndEvenDemo2 {
        private int printNo = 0;
        private Lock lock = new ReentrantLock(); // 不设置为公平锁
        private Condition condition = lock.newCondition();
        /**
         * 奇数打印方法,由奇数线程调用
         */
        public void odd() {
            while (printNo < 10) {
                lock.lock();
                try {
                    if (!isEvenNo(printNo)) {
                        System.out.println("奇数:" + printNo);
                        printNo ++;
                        condition.signal();
                    } else {
                        try {
                            condition.await();
                        } catch (Exception exception) {
                            exception.printStackTrace();
                        }
                    }
                } catch (Exception exception) {
                    exception.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }

        /**
         * 偶数打印方法,偶数线程调用
         */
        public void even() {
            while (printNo < 10) {
                lock.lock();
                try {
                    if (isEvenNo(printNo)) {
                        System.out.println("偶数:" + printNo);
                        printNo ++;
                        condition.signal();
                    } else {
                        try {
                            condition.await(); // 等待偶数线程执行完毕
                        } catch (Exception exception) {
                            exception.printStackTrace();
                        }
                    }
                } catch (Exception exception) {
                    exception.printStackTrace();
                }
            }
        }
    }

执行部分:

    /**
     * 使用Condition对象方法实现
     */
    @Test
    public void useCondition() {
        OddAndEvenDemo2 oddAndEvenDemo = new OddAndEvenDemo2();

        // 开启奇数线程
        Thread oddThread = new Thread(() -> oddAndEvenDemo.odd());
        // 开启偶数线程
        Thread evenThread = new Thread(() -> oddAndEvenDemo.even());
        oddThread.start();
        evenThread.start();
    }

 

- Object方法和Condition的区别总结:

1、Object锁对象基于同步关键字组合使用,等待与唤醒都是使用Object的wait & notify 且锁使用syncornized

2、Condition用于配合Lock对象组合使用,等待与唤醒使用 signal & await方法

 

 

二、指定数量等待 CountDownLatch

代码案例:

设置三个运动员线程和一个教练线程

只有等待三个运动员线程准备就绪之后,教练线程开始吹口哨开始训练

package cn.cloud9.test.multithread;


import java.util.concurrent.CountDownLatch;

/**
 *
 */
public class CountDownLatchDemo {
    
    static class CoachRacerDemo {
        private CountDownLatch cdl = new CountDownLatch(3); // 设置需要等待的线程数量
        
        /**
         * 运动员方法
         */
        public void racer() {
            // 获取线程名称
            String name = Thread.currentThread().getName();
            System.out.println(name + " is preparing ... ");
            try {
                Thread.sleep(1000);

            } catch (Exception exception) {
                exception.printStackTrace();
            }
            System.out.println(name + " is ready!");
            cdl.countDown();
        }

        /**
         * 教练方法
         */
        public void coach() {
            String name = Thread.currentThread().getName();
            System.out.println(name + " wait racer prepare ready ...");
            try {
                cdl.await();
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            System.out.println("all racer is ready! start training!");
        }

    }


    public static void main(String[] args) {
        CoachRacerDemo coachRacerDemo = new CoachRacerDemo();
        Thread racer1 = new Thread(() -> coachRacerDemo.racer(), "racer-01");
        Thread racer2 = new Thread(() -> coachRacerDemo.racer(), "racer-02");
        Thread racer3 = new Thread(() -> coachRacerDemo.racer(), "racer-03");

        Thread coach = new Thread(() -> coachRacerDemo.coach(), "coach");

        // coach线程会先等待其他线程执行,直到等待数量的线程都执行完毕之后开始继续执行
        coach.start();
        racer1.start();
        racer2.start();
        racer3.start();
    }
}

 

三、统一执行 CyclicBarrier

package cn.cloud9.test.multithread;

import java.util.Date;
import java.util.concurrent.CyclicBarrier;

/**
 * CyclicBarrier
 * 作用:
 *  让一组线程等待到某个状态之后,再全部同时执行
 *  CyclicBarrier底层基于 ReentrantLock和Condition实现
 *
 */
public class CyclicBarrierDemo {

    static class RunTogetherDemo {
        final CyclicBarrier cyclicBarrier = new CyclicBarrier(3); // 参与同时起跑的线程数

        public void startThread(int sec) {
            String name = Thread.currentThread().getName();
            System.out.println(name + " 正在准备...");
            try {
                Thread.sleep(sec);
                cyclicBarrier.await();
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            System.out.println(name + " 已经启动完毕:" + new Date().getTime());
        }
    }

    public static void main(String[] args) {
        final RunTogetherDemo cyclicBarrierDemo = new RunTogetherDemo();
        Thread thread1 = new Thread(() -> cyclicBarrierDemo.startThread(300));
        Thread thread2 = new Thread(() -> cyclicBarrierDemo.startThread(400));
        Thread thread3 = new Thread(() -> cyclicBarrierDemo.startThread(500));
        thread1.start();
        thread2.start();
        thread3.start();
    }

}

执行之后三个线程会在同一时刻开始执行await方法后的代码块

尽管之前让线程睡眠了不同时长,最后启动完毕的时间戳是一样的

 

 

四、资源访问控制 Semaphore

代码案例:

8个工人 使用 3台机器,机器为互斥资源(即每次只能让一个工人来操作)

package cn.cloud9.test.multithread;

import java.util.concurrent.Semaphore;

/**
 * 互斥案例
 */
public class SemaphoreDemo {

    /**
     *
     */
    static class WorkMachineDemo implements Runnable {
        private int worker;
        private Semaphore semaphore;

        public WorkMachineDemo(int worker, Semaphore semaphore) {
            this.worker = worker;
            this.semaphore = semaphore;
        }

        @Override
        public void run(){
            try {
                // 1、工人获取机器
                semaphore.acquire();
                // 2、打印工人获取到机器,开始工作
                String name = Thread.currentThread().getName();
                System.out.println(name + " 获取到机器,开始作业");
                // 3、线程睡眠一秒,模拟工人机器操作中
                Thread.sleep(1000);
                // 3、使用完毕,工人下机
                semaphore.release();
                System.out.println(name + " 作业完毕,工人下机");
            } catch (Exception exception) {
                exception.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        int worker = 8;
        Semaphore semaphore = new Semaphore(3);
        WorkMachineDemo workMachineDemo = new WorkMachineDemo(worker, semaphore);
        for (int i = 0; i < worker; i++) {
            new Thread(workMachineDemo).start();
        }
    }
}

 

 

 

posted @ 2021-12-03 15:19  emdzz  阅读(102)  评论(0)    收藏  举报