Java 中CountDownLatch、CyclicBarrier、Semaphore比较

4 篇文章 0 订阅

Java 中CountDownLatch、CyclicBarrier、Semaphore比较

jdk1.5之后,java的concurrent包提供了一些并发工具类,比如CountDownLatch和CyclicBarrier,Semaphore。这里简要的比较一下他们的共同之处与区别,同时介绍一下他们的使用场景。 CountDownLatch:一个线程A或是组线程A等待其它线程执行完毕后,一个线程A或是组线程A才继续执行。CyclicBarrier:一组线程使用await()指定barrier,所有线程都到达各自的barrier后,再同时执行各自barrier下面的代码。Semaphore:是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源

CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。

作用

CountDownLatch 的作用和 Thread.join() 方法类似,可用于一组线程和另外一组线程的协作。例如,主线程在做一项工作之前需要一系列的准备工作,只有这些准备工作都完成,主线程才能继续它的工作。这些准备工作彼此独立,所以可以并发执行以提高速度。在这个场景下就可以使用 CountDownLatch 协调线程之间的调度了。在直接创建线程的年代(Java 5.0 之前),我们可以使用 Thread.join()。在 JUC 出现后,因为线程池中的线程不能直接被引用,所以就必须使用 CountDownLatch 了。

public static void main(String[] args) {
        final CountDownLatch latch = new CountDownLatch(2);

        new Thread(){
            public void run() {
                try {
                    System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                    Thread.sleep(3000);
                    System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                    latch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        }.start();

        new Thread(){
            public void run() {
                try {
                    System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                    Thread.sleep(3000);
                    System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                    latch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        }.start();

        try {
            System.out.println("等待2个子线程执行完毕...");
            latch.await();
            System.out.println("2个子线程已经执行完毕");
            System.out.println("继续执行主线程");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
运行结果:

子线程Thread-0正在执行
等待2个子线程执行完毕...
子线程Thread-1正在执行
子线程Thread-1执行完毕
子线程Thread-0执行完毕
2个子线程已经执行完毕
继续执行主线程

CyclicBarrier字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。它主要的方法就是一个:await()。await() 方法被调用一次,计数便会减少1,并阻塞住当前线程。当计数减至0时,阻塞解除,所有在此 CyclicBarrier 上面阻塞的线程开始运行。在这之后,如果再次调用 await() 方法,计数就又会变成 N-1,新一轮重新开始,这便是 Cyclic 的含义所在。

public static void main(String[] args) {
		CyclicBarrier barrier = new CyclicBarrier(5, new Runnable() {
			//栅栏动作,在计数器为0的时候执行
			@Override
			public void run() {
				System.out.println("我们都准备好了.");
			}
		});
		
		ExecutorService es = Executors.newCachedThreadPool();
		for (int i = 0; i < 5; i++) {
			es.execute(new Roommate(barrier));
		}
	}
}

class Roommate implements Runnable {
	private CyclicBarrier barrier;
	private static int Count = 1;
	private int id;

	public Roommate(CyclicBarrier barrier) {
		this.barrier = barrier;
		this.id = Count++;
	}

	@Override
	public void run() {
		System.out.println(id + " : 我到了");
		try {
			Thread.sleep(((long)(Math.random() * 10000)));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		try {
			//通知barrier,已经完成动作,在等待
			barrier.await();
			System.out.println("Id " + id + " : 点菜吧!");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (BrokenBarrierException e) {
			e.printStackTrace();
		}
	}
运行结果:

1 : 我到了
2 : 我到了
3 : 我到了
4 : 我到了
5 : 我到了
我们都准备好了.
Id 5 : 点菜吧!
Id 1 : 点菜吧!
Id 3 : 点菜吧!
Id 2 : 点菜吧!
Id 4 : 点菜吧!


Semaphore翻译成字面意思为 信号量,Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。它还有 tryAcquire 和 acquireUninterruptibly 方法,可以根据自己的需要选择

private Semaphore semaphore;// fS号量
    private int user;// 第几个用户

    class MyTask implements Runnable {
        Semaphore semaphore;
        int user;
        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println("开始排队" + user);
                Thread.sleep((long)Math.random() * 1000);
                System.out.println(user + "排队结束,准备离开 " + user);
                Thread.sleep((long)Math.random() * 10000);
                System.out.println(user + "已经离开,下一个进场");
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public MyTask(Semaphore semaphore, int user) {
            this.semaphore = semaphore;
            this.user = user;
        }
    }
    private void execute(){

        final Semaphore s = new Semaphore(2);

        ExecutorService threadPool = Executors.newCachedThreadPool();

        for (int i = 0; i < 20; i++) {
            threadPool.execute(new MyTask(s, (i +1))); }
        threadPool.shutdown();
    }

    public static void main (String [ ] args) {
        ThreadDemo demo = new ThreadDemo();
        demo.execute();
    }
运行结果:
开始排队1
开始排队2
2排队结束,准备离开 2
1排队结束,准备离开 1
2已经离开,下一个进场
1已经离开,下一个进场
开始排队3
开始排队4
3排队结束,准备离开 3
4排队结束,准备离开 4
3已经离开,下一个进场
开始排队5
4已经离开,下一个进场
开始排队6
6排队结束,准备离开 6
5排队结束,准备离开 5
6已经离开,下一个进场
5已经离开,下一个进场
开始排队7
开始排队8
7排队结束,准备离开 7
8排队结束,准备离开 8
7已经离开,下一个进场
开始排队9
8已经离开,下一个进场
开始排队10
9排队结束,准备离开 9
10排队结束,准备离开 10
9已经离开,下一个进场
10已经离开,下一个进场
开始排队11
开始排队12
11排队结束,准备离开 11
12排队结束,准备离开 12
11已经离开,下一个进场
开始排队13
12已经离开,下一个进场
开始排队14
13排队结束,准备离开 13
14排队结束,准备离开 14
13已经离开,下一个进场
开始排队15
14已经离开,下一个进场
开始排队16
15排队结束,准备离开 15
16排队结束,准备离开 16
15已经离开,下一个进场
开始排队17
16已经离开,下一个进场
17排队结束,准备离开 17
开始排队18
17已经离开,下一个进场
开始排队19
18排队结束,准备离开 18
19排队结束,准备离开 19
18已经离开,下一个进场
19已经离开,下一个进场
开始排队20
20排队结束,准备离开 20
20已经离开,下一个进场

共同

  • CountDownLatch与CyclikBarrier两者的共同点是都具有await()方法,并且执行此方法会引起线程的阻塞,达到某种条件才能继续执行(这种条件也是两者的不同)。Semaphore,acquire方获取的资源达到最大数量时,线程再次acquire获取资源时,也会使线程处于阻塞状态。CountDownLatch与CyclikBarrier两者的共同点是都具有await()方法,并且执行此方法会引起线程的阻塞,达到某种条件才能继续执行(这种条件也是两者的不同)。Semaphore,acquire方获取的资源达到最大数量时,线程再次acquire获取资源时,也会使线程处于阻塞状态。CountDownLatch、CyclikBarrier、Semaphore 都有一个int类型参数的构造方法。
  • CountDownLatch、CyclikBarrier、Semaphore 都有一个int类型参数的构造方法。

示例下载: 点击打开链接



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值