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获取到打野位置(共享资源)符合预期。

  • 相关阅读:
    斐波那契数列 的两种实现方式(Java)
    单链表反转
    单链表合并
    两个有序list合并
    list去重 转载
    RemoveAll 要重写equals方法
    Java for LeetCode 138 Copy List with Random Pointer
    Java for LeetCode 137 Single Number II
    Java for LeetCode 136 Single Number
    Java for LeetCode 135 Candy
  • 原文地址:https://www.cnblogs.com/geekdc/p/13610451.html
Copyright © 2011-2022 走看看