java多线程协同

概述

并发协同就是多个线程共同完成一件事情,主要的实现方式就是根据实际情况堵塞某些线程,等待某些线程完成一定的工作之后再执行。

协同主要方法

  • 基础传统的Synchronized及Object的wait,notify,notifyAll等方法
  • 基于lock和Condition的wait
  • 使用并发的工具类,可以很方便灵活的实现线程的并发协同

协同要思考的问题

  • 什么地方需要协同
  • 什么线程在什么时候需要等待

常用API(java并发包中的)

CountDownLatch

CountDownLatch是一个倒数的计数器,初始化时设置一个值,子线程完成后,计数器归零时则不再阻塞。这样可以保证在一定数量的线程执行完毕后再执行后续的操作。

主要用法
  • 构造函数CountDownLatch(int count),指定等待线程的数量
  • 等待方法await();当计数大于0时会阻塞线程,计数为0时不会阻塞
  • 条件完成减数方法countDown();
  • boolen await(long timeout,TimeUnit unit)设置等待的最大时长,返回true表示count清0了,false表示count不为0,但时间到了
  • long getCount(),获取当前的计数
使用示例
	public static void main(String[] args) throws InterruptedException {
	    CountDownLatch latch=new CountDownLatch(5);
	    String[] names=new String[]{"1号","2号","3号","4号","5号"};
	    for(int i=0;i<5;i++){
	    	new Thread(new Runnable() {
				
				@Override
				public void run() {
					 try {
						Thread.sleep(3000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}//阻塞3s
					 System.out.println("我是"+Thread.currentThread().getName()+"已经到达");
					 latch.countDown();
				}
			},names[i]).start();
	    }
	    
	    latch.await();
	    System.out.println("所有人都到齐了");
	}

输出结果:

我是5号已经到达
我是3号已经到达
我是1号已经到达
我是4号已经到达
我是2号已经到达
所有人都到齐了

CyclicBarrier 循环屏障

指定要协同的线程数量,并构成一个屏障,让线程在这个屏障前等待,当等待的数量达到设置值时,屏障放开。放开后又可重新使用,所以是循环屏障。与CountDownLatch都能设置固定数量的线程协同,但是CountDownLatch是做减法,减完之后就没了,CyclicBarrier是做加法,加到一定数量放开屏障并能循环使用。

主要方法
  • 构造方法 CyclicBarrier(int parties) 指定屏障拦截的线程数
  • 构造方法 CyclicBarrier(int parties,Runable advice) 指定屏障拦截的线程数和等待线程数达到设置值时触发的一个方法
  • int await();线程调用此方法表示自己到达屏障前,若此时等待的线程数量不满足则此方法会造成当前线程阻塞,直到等待的线程数达到屏障的设置值
  • int await(long timeout,TimeUnit unit);设置等待的最大时间,若超过时间还没释放就抛出异常
  • int getNumberWaiting();获取等待在屏障前的线程数
  • boolean isBroken();屏障是否被破坏
  • void reset();重设屏障,如果屏障中存在等待的线程,则这些线程将会被释放,同时这个方法将抛出异常
使用示例
public static void main(String[] args){
	    CyclicBarrier barrier=new CyclicBarrier(5,()->{System.out.println("继续出发");});
	    String[] names=new String[]{"1号","2号","3号","4号","5号"};
	    for(int i=0;i<5;i++){
	        new Thread(new Runnable() {
	            				
					@Override
					public void run(){
						 try {
							Thread.sleep(3000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}//阻塞3s
						 System.out.println("我是"+Thread.currentThread().getName()+"已经到达");
						 try {
							barrier.await();
						} catch (InterruptedException e1) {
							e1.printStackTrace();
						} catch (BrokenBarrierException e1) {
							e1.printStackTrace();
						}
			            try {
							Thread.sleep(3000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}//阻塞3s
						 System.out.println("我是"+Thread.currentThread().getName()+"已经到达");
						 try {
							barrier.await();
						} catch (InterruptedException e1) {
							e1.printStackTrace();
						} catch (BrokenBarrierException e1) {
							e1.printStackTrace();
						}
						try {
							Thread.sleep(3000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}//阻塞3s
						 System.out.println("我是"+Thread.currentThread().getName()+"已经到达");
						 try {
							barrier.await();
						} catch (InterruptedException e) {
							e.printStackTrace();
						} catch (BrokenBarrierException e) {
							e.printStackTrace();
						}
					}
	        },names[i]).start();
	    }
}

输出结果:

我是2号已经到达
我是3号已经到达
我是4号已经到达
我是5号已经到达
我是1号已经到达
继续出发
我是1号已经到达
我是4号已经到达
我是3号已经到达
我是5号已经到达
我是2号已经到达
继续出发
我是4号已经到达
我是2号已经到达
我是3号已经到达
我是1号已经到达
我是5号已经到达
继续出发

Phaser 阶段协同器(以后补充)

Phaser是java7提供的一个协同工具类,包含了CountDownLatch和CyclicBarrier的特点。

主要方法
构造器
  • Phaser();初始任务为0
  • Phaser(int parties);指定初始任务
  • Phaser(Phaser parent);指定父协同器,子协同器会作为一个整体注册到父协同器中,如果子协同器中没有任务则会自动从父协同器中解除注册
  • Phaser(Phaser parent,int parties);指定父协同器,自己的任务数
增减任务
  • int register();增加一个任务,返回当前的阶段号
  • int bulkRegister(int parties);批量增加任务
  • int arriveAndDeregister();减少一个任务并返回当前阶段号
到达、等待
  • int arrive();到达(任务完成),返回当前阶段号
  • int arriveAndAwaitAdvance();到达后等待其他任务到达(和屏障的功能类似)
  • int awaitAdvance(int phase);在指定阶段等待(必须是当前阶段才有效)
  • int awaitAdvanceInteruptibly(int phase);
  • int awaitAdvanceInterruptibly(int phase,long timeout,TimeUnit unit);
阶段到达发出动作
  • protected boolean onAdvance(int phase,int registeredParties);
其他API
  • void forceTermination();强制结束
  • int getPhase();返回当前阶段号
  • boolean isTerminated();判断是否结束
使用示例(和CyclicBarrier类似)
public static void main(String[] args){
	    Phaser phaser=new Phaser(5);
	    String[] names=new String[]{"1号","2号","3号","4号","5号"};
	    for(int i=0;i<5;i++){
	        new Thread(new Runnable() {
	            				
					@Override
					public void run(){
						 try {
							Thread.sleep(3000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}//阻塞3s
						 System.out.println("我是"+Thread.currentThread().getName()+"已经到达第一阶段");
						 try {
							phaser.arriveAndAwaitAdvance();
						} catch (InterruptedException e1) {
							e1.printStackTrace();
						} catch (BrokenBarrierException e1) {
							e1.printStackTrace();
						}
			            try {
							Thread.sleep(3000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}//阻塞3s
						 System.out.println("我是"+Thread.currentThread().getName()+"已经到达第二阶段");
						 try {
							phaser.arriveAndAwaitAdvance();
						} catch (InterruptedException e1) {
							e1.printStackTrace();
						} catch (BrokenBarrierException e1) {
							e1.printStackTrace();
						}
						try {
							Thread.sleep(3000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}//阻塞3s
						 System.out.println("我是"+Thread.currentThread().getName()+"已经到达第三阶段");
						 try {
							phaser.arriveAndAwaitAdvance();
						} catch (InterruptedException e) {
							e.printStackTrace();
						} catch (BrokenBarrierException e) {
							e.printStackTrace();
						}
					}
	        },names[i]).start();
	    }
}

输出结果:

我是2号已经到达第一阶段
我是3号已经到达第一阶段
我是4号已经到达第一阶段
我是5号已经到达第一阶段
我是1号已经到达第一阶段
我是1号已经到达第二阶段
我是4号已经到达第二阶段
我是3号已经到达第二阶段
我是5号已经到达第二阶段
我是2号已经到达第二阶段
我是4号已经到达第三阶段
我是2号已经到达第三阶段
我是3号已经到达第三阶段
我是1号已经到达第三阶段
我是5号已经到达第三阶段

Semaphone计数信号量

Semaphone维护着一个许可的集合,有一定的初始许可数量,线程可以从中获取一个或多个许可,执行完操作后可以释放许可。Semaphone这样可以用来控制并发量,只有获取到许可的线程才能执行。

主要方法
  • Semaphone(int parmits):permits初始许可数,非公平获取
  • Semaphone(int parmits, boolean fair):fair=true公平获取,fair=false非公平获取
  • acquire(int coount);获取许可
  • tryAcquire();
  • release();释放许可

使用示例

public static void main(String[] args){
    Semaphone semaphone=new Semaphone(20);
    Lock lock=new ReentrantLock();
    for(int i=0;i<5;i++){
        new Thread(()->{
            lock.lock();
            semaphore.acquire(1);
            .....
            semaphore.release(1);
            lock.unlock();
        }).start();
    }
}

总结

  • CountDownLatch、CyclicBarrier、Phaser都可等待后一起执行
  • CyclicBarrier、Phaser可以有多个阶段等待后一起执行
  • CyclicBarrier、Phaser都可以有多个阶段,但是CyclicBarrier的参与数是固定的,Phaser可以在中途添加和修改参与数
  • Semaphone可以用来控制并发数量
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值