同步工具类

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

阻塞队列

保存对象的容器, 还能协调生产者和消费者等线程之间的控制流 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方法,这个方法将阻塞直到所有线程都到达栅栏位置。

原文地址:https://www.cnblogs.com/lucare/p/8679145.html