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

     

  • 相关阅读:
    xampp 80端口被占用后这么办??解决了
    XAMPP配置基于虚拟目录、多域名的环境
    mysql 主从同步
    jquery插件
    Css绘制箭头实现代码
    Ubuntu下mount命令的好用处
    linux下IPTABLES配置详解
    java程序员网站
    1.Hibernate介绍
    1. Mybatis介绍
  • 原文地址:https://www.cnblogs.com/mindzone/p/15634976.html
Copyright © 2020-2023  润新知