四、Semaphore
Semaphore 表示信号量,初始化对象的时候,需要传一个参数,表示信号量的计数器值。acquire() 方法将计数器加 1,release() 方法减 1,这两个方法都能够保证原子性。
信号量的简单示例:
public class SemaphoreTest { private final Semaphore semaphore = new Semaphore(1); private int value; public void addValue() { try { semaphore.acquire(); value += 1; } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } }
程序中使用信号量实现了一个线程安全的方法,初始值设为了 1,当多个方法访问 addValue 方法的时候,由于 acquire 方法保证原子性,所以只能有一个线程将计数器减 1 并进入临界区,另一个线程等待。
一个线程执行完后,调用 release 方法,计数器加 1,另一个等待的线程被唤醒。
Semaphore 与 Lock 的一个不同点便是信号量允许多个线程同时进入临界区,例如将初始值设置的更大一些。例如下面这个例子:
public class SemaphoreTest { //初始值 2,表示 2 个线程可同时进入临界区 private final Semaphore semaphore = new Semaphore(2); public void test() { try { semaphore.acquire(); System.out.println("线程" + Thread.currentThread().getName() + " 进入临界区 : " + System.currentTimeMillis()); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } } }
五、CountDownLatch
CountDownLatch 是一个线程同步的工具,主要实现一个线程等待多个线程的功能。在原始的 Thread 中,可以调用 join() 方法来等待线程执行完毕,而 CountDownLatch 则可以用在线程池中的线程等待。
下面是 CountDownLatch 的使用示例:
public class CountDownLatchTest { //实际生产中不推荐使用这种创建线程的方式 private final ExecutorService threadPool = Executors.newFixedThreadPool(2); public void test() throws InterruptedException { CountDownLatch latch = new CountDownLatch(2); threadPool.execute(() -> { System.out.println("线程1执行完毕"); latch.countDown(); }); threadPool.execute(() -> { System.out.println("线程2执行完毕"); latch.countDown(); }); latch.await(); System.out.println("两个线程都执行完毕"); threadPool.shutdown(); } }
CountDownLatch 的初始值为 2,线程执行完毕则调用 countDown 方法,计数器减 1。减到 0 的时候,会唤醒主线程继续执行。
六、CyclicBarrier
CyclicBarrier 也是一个线程同步工具类,主要实现多个线程之间的互相等待。
CyclicBarrier 有两个构造函数,可以传一个计数器的初始值,还可以加上一个 Runnable,表示计数器执行减到 0 的时候,需要执行的回调方法。
public class CyclicBarrierTest { private final ExecutorService threadPool = Executors.newFixedThreadPool(2); private final CyclicBarrier barrier = new CyclicBarrier(2, this::note); public void print(){ threadPool.execute(() -> { System.out.println("线程1执行完毕"); try { barrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } }); threadPool.execute(() -> { System.out.println("线程2执行完毕"); try { barrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } }); threadPool.shutdown(); } public void note(){ System.out.println("两个线程执行完毕"); } }
示例中设置 CyclicBarrier 的初始值为 2,线程执行完毕调用 await 方法,计数器减 1。print() 方法中的两个线程执行完后,计数器减到 0,就会调用 note 方法。