闭锁,信号量和栅栏

孤街浪徒 提交于 2020-08-18 09:03:29

前言

jdk1.5并发库新增同步工具类,包括闭锁,信号量,循环栅栏。其本质不过是声明一个int类型volatile的变量state,来记录状态,也就是个数,通过不断的循环读取这个值,和同步的减小这个值,达到0,以达到阻塞线程和满足条件时放行的目的。

闭锁CountDownLatch

功能说明

一个例子

public class CountDownTest {
    private CountDownLatch countDownLatch = new CountDownLatch(3);

    public static void main(String[] args) throws InterruptedException {
        CountDownTest countDownTest = new CountDownTest();
        countDownTest.test();
        System.out.println("-----验证不会再改变状态,这扇门将永远保持打开-------");
        countDownTest.test();
    }

    public void test() throws InterruptedException {
        new Thread(() -> {
            try {
                Thread.sleep(1000);
                System.out.println("A搞到粉条回来了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();
            }
        }).start();
        new Thread(() -> {
            try {
                Thread.sleep(2000);
                System.out.println("B搞到白菜回来了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();
            }
        }).start();
        new Thread(() -> {
            try {
                Thread.sleep(1000);
                System.out.println("C搞到猪肉回来了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();
            }
        }).start();
        System.out.println("我先开始烧火了。");
        countDownLatch.await();
        System.out.println("材料都到齐了,开始做。");
    }
}
我先开始烧火了。
A搞到粉条回来了
C搞到猪肉回来了
B搞到白菜回来了
材料都到齐了,开始做。
-----验证不会再改变状态,这扇门将永远保持打开-------
我先开始烧火了。
材料都到齐了,开始做。
A搞到粉条回来了
C搞到猪肉回来了
B搞到白菜回来了

源码浅析

volatile变量保持可见性,用来记录当前状态。

    /**
     * The synchronization state.
     */
    private volatile int state;

在构造方法中,实例化内部类Sync,设置state为count个。

    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
 protected final void setState(int newState) {
        state = newState;
    }

countDown()方法就是将state同步的减1。

public void countDown() {
    sync.releaseShared(1);
}
protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }

await()方法是一个死循环,阻塞当前进程,不断的尝试获取state的数量,如果state为0后就放行。

public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
  public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
            for (;;) {}
    }

信号量Semaphore

功能说明

一个例子

public class SemaphoreTest {
    private static final int MAX_SEATS = 10;
    private static final int PEOPLES = 15;
    private static Semaphore semaphore = new Semaphore(MAX_SEATS);
    ExecutorService executorService = Executors.newFixedThreadPool(20);

    public static void main(String[] args) {
        new SemaphoreTest().test();
    }

    public void test() {
        for (int i = 0; i < PEOPLES; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "获得许可进入");
                    Thread.sleep(2000);
                    semaphore.release();
                    System.out.println(Thread.currentThread().getName() + "吃完走了。");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }
}
pool-1-thread-1获得许可进入
pool-1-thread-2获得许可进入
pool-1-thread-3获得许可进入
pool-1-thread-4获得许可进入
pool-1-thread-5获得许可进入
pool-1-thread-6获得许可进入
pool-1-thread-7获得许可进入
pool-1-thread-8获得许可进入
pool-1-thread-9获得许可进入
pool-1-thread-10获得许可进入
pool-1-thread-1吃完走了。
pool-1-thread-11获得许可进入
pool-1-thread-2吃完走了。
pool-1-thread-12获得许可进入
pool-1-thread-3吃完走了。
pool-1-thread-4吃完走了。
pool-1-thread-14获得许可进入
pool-1-thread-13获得许可进入
pool-1-thread-5吃完走了。
pool-1-thread-15获得许可进入
pool-1-thread-6吃完走了。
pool-1-thread-7吃完走了。
pool-1-thread-8吃完走了。
pool-1-thread-9吃完走了。
pool-1-thread-10吃完走了。
pool-1-thread-11吃完走了。
pool-1-thread-12吃完走了。
pool-1-thread-13吃完走了。
pool-1-thread-14吃完走了。
pool-1-thread-15吃完走了。

循环栅栏CyclicBarrier

功能说明

一个例子

public class CyclicBarrierTest {
	//转载,可能源自张孝祥老师
    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        // 创建CyclicBarrier对象并设置3个公共屏障点
        final CyclicBarrier cb = new CyclicBarrier(3);
        System.out.println("创建子线程开始");
        for (int i = 0; i < 3; i++) {
            Runnable runnable = () -> {
                try {
                    Thread.sleep((long) (Math.random() * 10000));
                    System.out.println(
                            "线程" + Thread.currentThread().getName() + "即将到达集合地点1,当前已有" + (cb.getNumberWaiting() + 1)
                                    + "个已到达" + (cb.getNumberWaiting() == 2 ? "都到齐了,继续走啊" : "正在等候"));
                    try {
                        cb.await();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                    Thread.sleep((long) (Math.random() * 10000));
                    System.out.println(
                            "线程" + Thread.currentThread().getName() + "即将到达集合地点2,当前已有" + (cb.getNumberWaiting() + 1)
                                    + "个已到达" + (cb.getNumberWaiting() == 2 ? "都到齐了,继续走啊" : "正在等候"));
                    try {
                        cb.await();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                    Thread.sleep((long) (Math.random() * 10000));
                    System.out.println(
                            "线程" + Thread.currentThread().getName() + "即将到达集合地点3,当前已有" + (cb.getNumberWaiting() + 1)
                                    + "个已到达" + (cb.getNumberWaiting() == 2 ? "都到齐了,继续走啊" : "正在等候"));
                    try {
                        cb.await();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
            service.execute(runnable);
            System.out.println("子线程 "+ i + "提交到线程池中");
        }
        System.out.println("所有子线程提交完毕,线程池关闭,但线程池关闭的真实时间为所有子线程都执行完毕后");
        service.shutdown();//线程池的状态则立刻变成SHUTDOWN状态,以后不能再往线程池中添加任何任务,否则将会抛出RejectedExecutionException异常。但是,此时线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。 与它相似的还有一个shutdownNow(),它通过调用Thread.interrupt来实现线程的立即退出。
    }
}
创建子线程开始
子线程 0提交到线程池中
子线程 1提交到线程池中
子线程 2提交到线程池中
所有子线程提交完毕,线程池关闭,但线程池关闭的真实时间为所有子线程都执行完毕后
线程pool-1-thread-3即将到达集合地点1,当前已有1个已到达正在等候
线程pool-1-thread-1即将到达集合地点1,当前已有2个已到达正在等候
线程pool-1-thread-2即将到达集合地点1,当前已有3个已到达都到齐了,继续走啊
线程pool-1-thread-3即将到达集合地点2,当前已有1个已到达正在等候
线程pool-1-thread-1即将到达集合地点2,当前已有2个已到达正在等候
线程pool-1-thread-2即将到达集合地点2,当前已有3个已到达都到齐了,继续走啊
线程pool-1-thread-3即将到达集合地点3,当前已有1个已到达正在等候
线程pool-1-thread-2即将到达集合地点3,当前已有2个已到达正在等候
线程pool-1-thread-1即将到达集合地点3,当前已有3个已到达都到齐了,继续走啊
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!