CountDownLatch、Semaphone学习

学习博客笔记https://www.cnblogs.com/dolphin0520/p/3920397.html

1.CountDownLatch(倒计数器)

作用:latch(门闩),门闩的含义:把门锁起来,不让里面的线程跑出来,因此,这个工具通常用来控制线程等待,它可以让某个线程等待直到倒计时结束,再开始执行。

CountDownLatch的构造函数接收一个整数N作为参数,即当前这个计数器个数。

public CountLatch(int count)

每当调用一次countDown(),N就会减去1,CountDownLatch的await()会阻塞当前线程,直到N变为0,当前线程才会继续执行。

public class CountDownLatchTest {

    static CountDownLatch c = new CountDownLatch(2);     //初始化计数器个数为2

    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(1);
                c.countDown();				//计数器里面值减去一
                System.out.println(2);
                c.countDown();
            }
        }).start();

        c.await();                        //CountDownLatch的await()方法阻塞当前线程,计数到0时,当前线程继续执行
        System.out.println("3");
    }

}

输出:

1
2
3

小结

1)CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行

2)CountDownLatch通过一个计数器来实现的,计数器的初始值为线程的数量,每当一个线程完成了自己的任务后,计数器的只就减1,当计数器的值到达0时,表示所有的线程已经完成了任务,然后闭锁上等待的线程就可以恢复执行任务。

3)主线程必须在启动其他线程后立即调用CountDownLatch.await()方法,这样主线程就会在这个方法上拥塞,直到其他线程完成各自的任务。

4)其他线程在完成自己的任务后,需要通知CountDownLatch对象

5)注意计算器的值一直没有减为0,此时我们不能让主线程一直等待,所以可以使用await(long time,TimeUnit unit),等待特定的时间后,就不再阻塞当前线程。

2.CyclicBarrier(同步屏障)

作用:当一组线程到达一个屏障时被阻塞,知道最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续执行。

CyclicBarrier的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。

示例代码:

public class CyclicBarrierTest {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N);
        for(int i=0;i<N;i++)
            new Writer(barrier).start();
    }
    
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
            try {
                Thread.sleep(5000);      //以睡眠来模拟写入数据操作
                System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println("所有线程写入完毕,继续处理其他任务...");
        }
    }
}

输出:

线程Thread-1正在写入数据...
线程Thread-3正在写入数据...
线程Thread-2正在写入数据...
线程Thread-0正在写入数据...
线程Thread-3写入数据完毕,等待其他线程写入完毕
线程Thread-1写入数据完毕,等待其他线程写入完毕
线程Thread-0写入数据完毕,等待其他线程写入完毕
线程Thread-2写入数据完毕,等待其他线程写入完毕
所以线程写入完毕后,我优先执行
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...

CyclicBarrier还提供了更高级的构造函数,用于在线程到达屏障时,优先执行barrierAction。

public CyclicBarrier(int parties, Runnable barrierAction)
将上面的例子稍微改下:
public class CyclicBarrierTest {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N,new A());
        for(int i=0;i<N;i++)
            new Writer(barrier).start();
    }
    
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
            try {
                Thread.sleep(5000);      //以睡眠来模拟写入数据操作
                System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println("所有线程写入完毕,继续处理其他任务...");
        }
      
    }
    
    static class A implements Runnable {
        @Override
        public void run() {
            System.out.println("所以线程写入完毕后,我优先执行");
        }
    }
}
输出:
线程Thread-1正在写入数据...
线程Thread-3正在写入数据...
线程Thread-2正在写入数据...
线程Thread-0正在写入数据...
线程Thread-3写入数据完毕,等待其他线程写入完毕
线程Thread-1写入数据完毕,等待其他线程写入完毕
线程Thread-0写入数据完毕,等待其他线程写入完毕
线程Thread-2写入数据完毕,等待其他线程写入完毕
所以线程写入完毕后,我优先执行
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...

3.CyclicBarrier和CountDownLatch的区别

1)CyclicBarrier的计数器可以使用重复使用,在最初的4个线程越过barrier状态后,又可以用来进行新一轮的使用。

public class CyclicBarrierTest {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N);
        for(int i=0;i<N;i++) {
            new Writer(barrier).start();
        }
        try {
            Thread.sleep(25000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("CyclicBarrier重用");
        for(int i=0;i<N;i++) {
            new Writer(barrier).start();
        }
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
            try {
                Thread.sleep(5000);      //以睡眠来模拟写入数据操作
                System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
             
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"所有线程写入完毕,继续处理其他任务...");
        }
    }
}

2)CyclicBarrier相比CountDownLatch更强大,提供了其他有用的方法,比如isBroken()方法用来了解阻塞线程是否被中断。

代码示例:

public class CyclicBarrierTest3 {

    static CyclicBarrier c = new CyclicBarrier(2);

    public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    c.await();
                    System.out.println("这里没有被执行");
                } catch (Exception e) {
                	System.out.println("线程被中断");
                }
            }
        });
        thread.start();
        thread.interrupt();
        try {
            c.await();
        } catch (Exception e) {
            System.out.println("了解线程是否被中断:"+c.isBroken());
        }
    }
}
代码输出:
线程被中断
了解线程是否被中断:true

4.Semaphone(信号量)

作用:Semaphone是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。信号量可以理解为对锁的扩展,无论是内部锁synchronized还是重入锁ReentrantLock,一次只允许一个线程访问一个资源,而信号量却可以指定多个线程,同时访问同一个资源。

构造函数:

 public Semaphore(int permits)
public Semaphore(int permits, boolean fair)

permits执行信号量的准入数,即同时能申请多少个许可。

acquire()用来获取一个许可,若无许可能够获得,则会一直等待,知道获得许可。

release()用来释放许可。注意在释放许可之前,必须先获得许可。

代码示例:

public class SemaphoreTest {
    public static void main(String[] args) {
        int N = 8;            //工人数
        Semaphore semaphore = new Semaphore(5); //准入数
        for(int i=0;i<N;i++)
            new Worker(i,semaphore).start();
    }
     
    static class Worker extends Thread{
        private int num;
        private Semaphore semaphore;
        public Worker(int num,Semaphore semaphore){
            this.num = num;
            this.semaphore = semaphore;
        }
         
        @Override
        public void run() {
            try {
                semaphore.acquire();	//尝试获取一个准入的许可,若无法获取,就会线程等待
                System.out.println("工人"+this.num+"占用一个机器在生产...");
                Thread.sleep(2000);
                System.out.println("工人"+this.num+"释放出机器");
                semaphore.release();    //在线程访问结束后,释放一个许可    
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

输出:

工人1占用一个机器在生产...
工人0占用一个机器在生产...
工人5占用一个机器在生产...
工人3占用一个机器在生产...
工人4占用一个机器在生产...
工人4释放出机器
工人5释放出机器
工人7占用一个机器在生产...
工人3释放出机器
工人0释放出机器
工人6占用一个机器在生产...
工人1释放出机器
工人2占用一个机器在生产...
工人6释放出机器
工人7释放出机器
工人2释放出机器


猜你喜欢

转载自blog.csdn.net/chenkaibsw/article/details/80937189
今日推荐