同步工具类

来源:互联网 发布:电算化考试软件下载 编辑:程序博客网 时间:2024/06/05 04:44

同步工具类可以是任何一个对象,只要它根据其自身的状态来协调线程的控制流。

阻塞队列

保存对象的容器, 还能协调生产者和消费者等线程之间的控制流 take和put等方法将阻塞,直到队列达到期望的状态(队列即非空,也非满)。

闭锁

相当于一扇门:在闭锁到达结束状态之前,这扇门一直是关闭的,并且没有任何线程能通过,当到达结束状态时,这扇门会打开并允许所有线程通过。

到达结束状态后,将不会再改变状态,这扇门永远保持打开状态。

CountDownLatch(倒计数锁存器) 是一种灵活的闭锁实现
countDown方法递减计数器
await方法等待计数器达到零,阻塞直到计数器为零或者等待中的线程中断,或者等待超时

使用场景

示例:

public class TestHarness {    public long timeTasks(int nThreads, final Runnable task) throws InterruptedException {        final CountDownLatch startGate = new CountDownLatch(1);//起始门        final CountDownLatch endGate = new CountDownLatch(nThreads);//结束门        for (int i = 0; i < nThreads; i++) {            Thread t = new Thread(){                @Override                public void run() {                    try {                        startGate.await();//一直阻塞直到计数器为零                        try {                            task.run();                        }finally {                            endGate.countDown();                        }                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            };            t.start();        }        long start = System.nanoTime();        startGate.countDown();        endGate.await();        long end = System.nanoTime();        return end - start;    }}

FutureTask

也可以用作闭锁。

信号量

计数信号量用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量。还可以用来实现某种资源池,或者对容器加边界。

Semaphore中管理者一组虚拟的许可,在执行操作时可以首先获得许可,并在使用以后释放许可,如果没有许可,那么acquire将阻塞直到有许可(或者直到中断或者操作超时)。

release方法将返回一个许可给信号量,计算信号量的一种简化形式是二值信号量,即初始值为1的Semaphore。二值信号量可以用作互斥体,并具备不可重入的加锁语义:谁拥有这个唯一的许可,谁就拥有了互斥锁。

Semaphore可以用于实现资源池,例如数据库连接池。

使用Semaphore为容器设置边界:

public class BoundedHashSet<T> {    private final Set<T> set;    private final Semaphore sem;    public BoundedHashSet(int bound) {        this.set = Collections.synchronizedSet(new HashSet<T>());        sem = new Semaphore(bound);    }    public boolean add(T o) throws InterruptedException{        sem.acquire();        boolean wasAdded = false;        try {            wasAdded = set.add(o);            return wasAdded;        } finally {            if (!wasAdded)                sem.release();        }    }    public boolean remove(Object o) {        boolean wasRemoved = set.remove(o);        if (wasRemoved){            System.out.println(Thread.currentThread().getName()+" remove "+o);            sem.release();        }        return wasRemoved;    }}

栅栏

栅栏(Barrier)类似于闭锁,它能阻塞一组线程直到某个事件发生。

闭锁是一次性对象,一旦进入终止状态,就不能被重置。

栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程。

CyclicBarrier可以使一定数量的参与方反复地在栅栏位置汇集,它在并行迭代算法中非常有用:这种算法通常将一个问题拆分成一系列相互独立的子问题。

当线程到达栅栏位置时将调用await方法,这个方法将阻塞直到所有线程都到达栅栏位置。