zoukankan      html  css  js  c++  java
  • java并发工具类

    一、CountDownLatch

    字面意思:倒计时锁闩,该类可以实现一个线程在等其他多个线程执行完之后,继续执行。

    入参是一个计数器的值,当一个线程执行完毕时调用countDown()方法,计数器值会减1,当计数器值为0时,被await()阻塞的线程将被唤醒。

    CountDownLatch latch = new CountDownLatch(10);
    

    大家都玩过王者荣耀的5V5排位吧,当己方5个人准备就绪,对方5人也准备就绪时,才可以进入B/P环节,也就是王者荣耀这个线程需要等待10位玩家的线程都准备完毕,然后才出发进入游戏的操作。

    package com.duchong.concurrent;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 模拟5V5排位,10个玩家都准备就绪,才开始进入游戏
     * CountDownLatch :阻塞主线程,等子线程完成
     * @author DUCHONG
     * @since 2020-09-03 17:43:13
     */
    public class CountDownLatchDemo {
    
    
        public static final int playerNum=10;
    
        public static void main(String[] args) {
    
            final CountDownLatch latch = new CountDownLatch(10);
    
            GameThread subThread = new GameThread(latch);
            try {
    
                //模拟5V5排位
                for (int i = 1; i <= playerNum; i++) {
                    new Thread(subThread,"player-"+i).start();
                    TimeUnit.SECONDS.sleep(1L);
                }
                //阻塞主线程
                latch.await();
            }
            catch (InterruptedException e) {
            }
    
            System.out.println("王者荣耀:玩家全部准备就绪,开始进入游戏");
        }
    
    
        /**
         * 游戏子线程
         */
        static class GameThread implements Runnable {
    
            private CountDownLatch latch;
    
            public GameThread(CountDownLatch latch) {
                this.latch = latch;
            }
    
            @Override
            public void run() {
    
                try {
                    System.out.println(Thread.currentThread().getName()+"---准备就绪");
                }
                finally {
                    latch.countDown();
                }
            }
        }
    }
    

    结果

    image-20200903201235257

    二、CyclicBarrier

    字面意思: 循环屏障,多个线程到达一个屏障点时被阻塞,直到最后一个线程到达屏障点时,屏障才会解除,所有被屏障拦截的线程继续运行。

    第一个入参代表屏障接触时阻塞线程的数量。第二个入参代表屏障解除时要进行的操作

    CyclicBarrier c = new CyclicBarrier(10, ()->System.out.println("屏障解除"));
    

    await()方法其实是调用Conditionawait()

    public class CyclicBarrier {
    /** The lock for guarding barrier entry */
    private final ReentrantLock lock = new ReentrantLock();
    /** Condition to wait on until tripped */
    private final Condition trip = lock.newCondition();
    
        //....省略
        public int await() throws InterruptedException, BrokenBarrierException {
                try {
                    return dowait(false, 0L);
                } catch (TimeoutException toe) {
                    throw new Error(toe); // cannot happen
                }
            }
    
        //wait方法
        private int dowait(boolean timed, long nanos)
            throws InterruptedException, BrokenBarrierException,
        TimeoutException {
            //重入锁
            final ReentrantLock lock = this.lock;
            //加锁
            lock.lock();
            try {
                final Generation g = generation;
    
                if (g.broken)
                    throw new BrokenBarrierException();
    
                if (Thread.interrupted()) {
                    breakBarrier();
                    throw new InterruptedException();
                }
    
                int index = --count;
                if (index == 0) {  // tripped
                    boolean ranAction = false;
                    try {
                        final Runnable command = barrierCommand;
                        if (command != null)
                            command.run();
                        ranAction = true;
                        nextGeneration();
                        return 0;
                    } finally {
                        if (!ranAction)
                            breakBarrier();
                    }
                }
    
                // 循环
                for (;;) {
                    try {
                        if (!timed)
                            // 调用condition的await()方法
                            trip.await();
                        else if (nanos > 0L)
                            nanos = trip.awaitNanos(nanos);
                    } catch (InterruptedException ie) {
                        if (g == generation && ! g.broken) {
                            breakBarrier();
                            throw ie;
                        } else {
                            // We're about to finish waiting even if we had not
                            // been interrupted, so this interrupt is deemed to
                            // "belong" to subsequent execution.
                            Thread.currentThread().interrupt();
                        }
                    }
    
                    if (g.broken)
                        throw new BrokenBarrierException();
    
                    if (g != generation)
                        return index;
    
                    if (timed && nanos <= 0L) {
                        breakBarrier();
                        throw new TimeoutException();
                    }
                }
            } finally {
                //解锁
                lock.unlock();
            }
        }
        //....省略
    }
    

    该类同样可以实现与CountDownLatch相同的效果

    package com.duchong.concurrent;
    
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 模拟5V5排位,10个玩家都准备就绪,才开始进入游戏
     * CyclicBarrier :阻塞子线程,当等待中的子线程数到达一定数量时,跳闸。
     * @author DUCHONG
     * @since 2020-09-03 17:41:35
     */
    public class CyclicBarrierDemo {
    
        public static final int playerNum=10;
        /**
         * 屏障,初始10 当await()的线程数量达到10时,跳闸。
         */
        static CyclicBarrier c = new CyclicBarrier(playerNum, ()->System.out.println("王者荣耀:玩家全部准备就绪,开始进入游戏"));
    
    
        public static void main(String[] args) {
    
            try {
                //
                for (int i = 1; i <= playerNum; i++) {
                    new Thread(new GameThread(),"player-"+i).start();
                    TimeUnit.SECONDS.sleep(1L);
                }
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 游戏子线程
         */
        static class GameThread implements Runnable {
    
            @Override
            public void run() {
    
                try {
                    System.out.println(Thread.currentThread().getName()+"---准备就绪");
                    //阻塞子线程
                    c.await();
    
                    System.out.println(Thread.currentThread().getName()+"---进入游戏");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
    
            }
        }
    
    }
    

    结果

    image-20200903201627948

    三、Semaphore

    字面意思:信号量,多个线程访问一个共享资源时,如果想控制对该资源访问的线程的数量,可以用这个工具类。

    入参对象是一个许可的数量,如果数量大于1,则可以作为共享锁来使用,如果数量等于1,则可以作为排他锁来使用

    acquire()方法表示得到一个许可,可以对共享资源进行操作, 如果许可数量分配完了,其他线程将阻塞, 直到已得到许可的线程释放许可后,才有机会再获取许可。

    release()方法表示释放一个许可。

    Semaphore semaphore=new Semaphore(5);
    

    接着王者荣耀排位5V5的例子来讲,当玩家进入B/P环节,5个人,但是地图只有上中下三路,也就是说最多2个人去打野位置

    package com.duchong.concurrent;
    
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 模拟进入5V5排位游戏后的B/P环节,五个人,上中下三路,最多2个人去打野位置
     * Semaphore,对资源的并发数控制
     * @author DUCHONG
     * @since 2020-09-03 15:46
     **/
    public class SemaphoreDemo {
    
        //打野人数
        public static final int wildNum=2;
        //总共人数
        public static final int totalNum=5;
    
        static Semaphore semaphore=new Semaphore(wildNum);
    
        public static void main(String[] args) {
    
            try {
                for (int i = 1; i <=totalNum; i++) {
    
                    new Thread(new GameThread(semaphore),"player-"+i).start();
                    TimeUnit.SECONDS.sleep(1L);
                }
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    
        /**
         * 游戏子线程
         */
        static class GameThread implements Runnable {
    
            private Semaphore semaphore;
    
            public GameThread(Semaphore semaphore){
                this.semaphore=semaphore;
            }
            @Override
            public void run() {
    
                try {
                    //抢到打野位置,最多两个人,其他人想选打野位时阻塞,除非抢到的人选择其他路
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"---抢到打野位");
    
                    TimeUnit.SECONDS.sleep(3L);
                    System.out.println(Thread.currentThread().getName()+"---犹豫了一下,选择了其他路");
                }
                catch (Exception e){
    
                }
                finally {
                    //新增一个许可
                    semaphore.release();
                }
    
            }
        }
    }
    

    结果

    image-20200903202347884

    同一时刻只有两个player获取到打野位置(共享资源)符合预期。

  • 相关阅读:
    数据的独立同分布检验
    基于密度聚类的DBSCAN和kmeans算法比较
    Python 爬虫笔记、多线程、xml解析、基础笔记(不定时更新)
    多进程之multiprocessing模块、守护进程、互斥锁
    程序与进程的区别,并发与并行的区别,多进程的实现原理
    并发编程之守护进程
    MySQL帮助文档的使用
    MySQL操作之DCL
    MySQL操作之DML
    MySQL操作之DDL
  • 原文地址:https://www.cnblogs.com/geekdc/p/13610451.html
Copyright © 2011-2022 走看看