您的位置:首页 > 编程语言 > Java开发

Java并发编程之——同步器工具类(semaphores, barriers, CountDownLatch,exchangers)

2013-06-15 18:33 543 查看
Semaphore:

类java.util.concurrent.Semaphore提供了一个计数信号量,从概念上讲,信号量维护了一个许可集,如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可。每个release()添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore只对可用许可的号码进行计数,并采取相应的行动。调用acquire()时无法保持同步锁,因为这会阻止将数据项返回到池中。信号量封装所需的同步,以限制对池的访问,这同维持该池本身一致性所需的同步是分开的。将信号量初始化为1,使得它在使用时最多只有一个可用的许可,从而可用作一个相互排斥的锁。这通常也称为二进制信号量,因为它只能有两种状态。按此方式使用,二进制信号量具有某种属性(与很多Lock实现不同),即可以由线程释放“锁”,而不是由所有者(因为信号量没有所有权的概念)。在某些专门的上下文(如死锁恢复)中这会很有用。

Semaphore可以实现信号灯,可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数

Semaphore sp = new Semaphored(个数);

sp.availablePermits()当前可获得个数

sp.acquire();取得信号灯

sp.release();释放信号灯

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SemaphoreTest {

/**
* @param args
*/
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
//最多3个许可,也就最多有3个线程同时访问
final Semaphore sp = new Semaphore(3);
for(int i=0;i<10;i++){
Runnable runable = new Runnable() {
@Override
public void run() {
try{
//取得sq许可
sp.acquire();
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"进入");
System.out.println("当前还有"+(3-sp.availablePermits())+"并发");
//释放许可
sp.release();
}
};
service.execute(runable);

}
service.shutdown();
}

}


Barrier

在实际应用中,有时候需要多个线程同时工作以完成同一件事情,而且在完成过程中,往往会等所有线程都到达某一个阶段后再统一执行。这是CyclicBarrier很有用。它允许一组线程互相等待,直到到达某个公共屏障点。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时CyclicBarrier很有用。因为该barrier在释放等待线程后可以重用,所以称它为循环的barrier.它重要的属性就是参与者个数,另外最重要的方法是await(),当所有线程都调用了await()后,就表示这些线程可以继续执行,否则就会等待。CyclicBarrier支持一个可选 的Runnable命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作有用。

CyclicBarrier:表示大家彼此等待,大家集合好后才开始出发,就是一定数组的线程在一些集合点汇合,只有所有线程都执行到后才开始下一步。

CyclicBarrier cb = new CyclicBarrier(个数3);

cb.await();

cb.await(); cb.getNumberWaiting();

cb.await();

public class CyclicBarrierTest {

public static void main(String[] args) throws IOException, InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(3);

ExecutorService executor = Executors.newFixedThreadPool(3);
executor.submit(new Thread(new Runner(barrier, "1号选手")));
executor.submit(new Thread(new Runner(barrier, "2号选手")));
executor.submit(new Thread(new Runner(barrier, "3号选手")));

executor.shutdown();
}
}

class Runner implements Runnable {
// 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)
private CyclicBarrier barrier;

private String name;

public Runner(CyclicBarrier barrier, String name) {
super();
this.barrier = barrier;
this.name = name;
}

@Override
public void run() {
try {
Thread.sleep(1000 * (new Random()).nextInt(8));
System.out.println(name + " 准备好了...");
// barrier的await方法,在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(name + " 起跑!");
}
}


CountDownLatch

在完成一组正在其他线程 中执行的操作之前,它允许一个或多个线程一直等待。用给定的数字作为计数器初始化CountDownLatch,一个线程调用await()方法后,在当前计数到达零之前,会一直受阻塞。其它线程调用countDown()方法,会使计数器减一,所以,计数器的值 为0后,会释放所有等待的线程。其它后续的await()调用都将立即返回。这种现象只出现一次,因为计数无法被重置。如果需要重置计数,请考虑使用CyclicBarrier.

CountDownLatch作为一个通用同步工具,有很多用途。使用1初始化的CountDownLatch用作一个简单的开、关锁存器,或入口:在通过调用countDown()的线程打开入口前,所有调用await()的线程都一直在入口处等待。用N初始化的CountDownLatch可以使一个线程在N个线程完成某项操作之前一直等待,或者使其在某项操作完成N次之前一直等待。

CountDownLatch:犹如倒计时器计数器,调用CountDownLatch对象的countDown

方法就将计算器减1,当计数到达0时,则所有等待者或单个等待者开始执行。

CountDownLatch cdOrder = new CountDownLatch(3);

Exchanger

java.util.concurrent.Exchanger提供了一个同步点,在这个同步点,一对线程可以交换数据。每个线程通过exchange()方法的入口提供数据给他的伙伴线程,并接收他的伙伴线程提供的数据,并返回。

Exchager用于实现两个人之间的数据交换,每个人在完成一定的事务后与对方交换数据,第一个先拿出数据的人将一直等待第二个人拿到数据到来时,才能彼此交换数据
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: