# JUC：同步辅助类

`java.util.concurrent` 包下，有一些关于同步的辅助工具类，比如CountDownLatch、CyclicBarrier等。

### CountDownLatch

允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。CountDownLatch允许创建时传入一个初始值，代表门闩的数量，cdl调用await方法进行阻塞；cdl通过 `countDown()` 方法拿掉一个门闩，直到门闩的数量变为0时，await阻塞结束。

```java
public class CountDownLatchDemo {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch cdl = new CountDownLatch(5);
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                try {
                    Thread.sleep(finalI * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "执行完毕");
                cdl.countDown();
            }, "线程" + i).start();
        }
        cdl.await();
        System.out.println("所有门闩已经打开，继续执行main线程！");
    }

}
```

### CyclicBarrier

Cyclic 循环的，Barrier 栅栏，屏障。类似满人发车，每凑满20人，才会继续执行，下面是使用示例：

```java
public class CyclicBarrierDemo {

    public static void main(String[] args) {
        // runnable 是可选参数，代表达到数量后要执行的代码
        CyclicBarrier cyclicBarrier = new CyclicBarrier(20, new Runnable() {
            @Override
            public void run() {
                System.out.println("已经20个人了，发车！");
            }
        });
        for (int i = 0; i < 100; i++) {
            int finalI = i;
            new Thread(() -> {
                System.out.println("i:" + finalI);
                try {
                    cyclicBarrier.await(); // 等待发车，当满20个才会继续执行
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
                // 其他业务逻辑
            }, "线程" + i).start();
        }
    }
}
```

CyclicBarrer 应用于多个线程并发执行，比如网络操作、文件操作同时执行；当所有线程都准备完毕，下面的操作才可进行。

### Phaser

阶段执行，jdk7引入。多个线程执行一个任务，此任务分为多个阶段，每个阶段要求全部或者部分线程并发执行并且所有目标线程执行完毕后才可继续执行下一阶段的场景。比如达尔文遗传算法。下面使用Phaser对结婚场景进行模拟，结婚要按照阶段顺序依次执行，先到达、再拜堂、再吃酒席…：

```java
public class PhaserDemo {

    static MarriagePhaser phaser = new MarriagePhaser();

    // 定义结婚阶段类
    static class MarriagePhaser extends Phaser {
        // 定义每个阶段要执行的任务, 返回值为为true时，代表phaser终止，否则继续执行
        @Override
        protected boolean onAdvance(int phase, int registeredParties) {
            switch (phase) {
                case 0:
                    System.out.println("所有人都到齐了！共有" + registeredParties + "人");
                    System.out.println();
                    return false;
                case 1:
                    System.out.println("新郎新娘拜堂！共有" + registeredParties + "人");
                    System.out.println();
                    return false;
                case 2:
                    System.out.println("所有人酒席吃完了！共有" + registeredParties + "人");
                    System.out.println();
                    return false;
                case 3:
                    System.out.println("所有人都离开了！共有" + registeredParties + "人");
                    System.out.println();
                    return false;
                case 4:
                    System.out.println("新郎新娘入洞房！共有" + registeredParties + "人");
                    System.out.println();
                    return true;
            }

            return super.onAdvance(phase, registeredParties);
        }
    }

    // 定义人线程
    static class Person extends Thread {

        Person (String name) {
            setName(name);
        }

        // 到达现场
        private void arrive() {
            System.out.println(getName() + "到达");
            phaser.arriveAndAwaitAdvance(); // 到达并等待
        }
        // 拜堂
        private void chapel() {
            if (getName().equals("新郎") || getName().equals("新娘")) {
                System.out.println(getName() + "拜堂了");
                phaser.arriveAndAwaitAdvance();
            } else {
                phaser.arriveAndDeregister();
            }
        }
        // 酒席
        private void eat() {
            if (!getName().equals("新郎") && !getName().equals("新娘")) {
                phaser.register();
            }
            System.out.println(getName() + "吃了");
            phaser.arriveAndAwaitAdvance();
        }
        // 离开
        private void leave() {
            System.out.println(getName() + "离开了");
            phaser.arriveAndAwaitAdvance();
        }
        // 洞房
        private void papapa() {
            if (getName().equals("新郎") || getName().equals("新娘")) {
                System.out.println(getName() + "洞房了");
                phaser.arriveAndAwaitAdvance();
            }else {
                phaser.arriveAndDeregister();
            }
        }
        @Override
        public void run() {
            arrive();
            chapel();
            eat();
            leave();
            papapa();
        }
    }

    public static void main(String[] args) {
        phaser.bulkRegister(7);
        for(int i=0; i<5; i++) {
            new Thread(new Person("p" + i)).start();
        }

        new Thread(new Person("新郎")).start();
        new Thread(new Person("新娘")).start();
    }
}
```

执行结果如下：

```java
p0到达
新娘到达
新郎到达
p4到达
p3到达
p2到达
p1到达
所有人都到齐了！共有7人

新郎拜堂了
p0吃了
p2吃了
p4吃了
新娘拜堂了
p3吃了
p1吃了
新郎新娘拜堂！共有7人

新娘吃了
新郎吃了
p3离开了
p0离开了
p1离开了
p4离开了
p2离开了
所有人酒席吃完了！共有7人

新娘离开了
新郎离开了
所有人都离开了！共有2人

新郎洞房了
新娘洞房了
新郎新娘入洞房！共有2人
```

### Semaphore

Semaphore 是信号量的意思，也成为信号灯（红绿灯），用来保证两个或多个关键代码段不被并发调用；类似锁，但是可以限制并发线程的数量。常用于限流。

```java
public class SemaphoreDemo {

    public static void main(String[] args) {

        // 1盏灯，同时只允许一个线程执行
        Semaphore semaphore = new Semaphore(1);
        for (int i = 0; i < 3; i++) {
            int finalI = i;
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println("T"+ finalI +" run...");
                    Thread.sleep(200);
                    System.out.println("T"+ finalI +" run...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
            }).start();
        }
    }
}
```

### Exchanger

交换两个线程的信息（只能是两个线程）。

```java
// 交换线程0与线程1的装备
public class ExchangerDemo {

    public static void main(String[] args) {
        Exchanger<String> exchanger = new Exchanger<>();
        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                try {
                    String threadName = Thread.currentThread().getName();
                    String exchange = exchanger.exchange( threadName + "的装备");
                    System.out.println(threadName + " = " + exchange);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "线程" + i).start();
        }
    }

}
```

结果：

```
线程0 = 线程1的装备
线程1 = 线程0的装备
```

### LockSupport

LockSupport 是比较低级的工具方法，不过使用起来也很方便，相比notify，它可以指定某个thread进行唤醒。

```java
public class LockSupportDemo {

    public static void main(String[] args) throws InterruptedException {
        // LockSupport 拥有park与unpark方法，
        // park的含义为停车，调用此方法的线程将会进入阻塞状态
        // unpark 则可用于唤醒已经park的线程，此方法可以指定要唤醒的线程，比wait和notify更加灵活

        Thread t = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("---" + i + "---");
                if (i == 5) {
                    LockSupport.park();
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        // 注意，unpark将会将线程的park状态置为unpark
        // 调用一次后，再次调用park则无法进行线程阻塞
        // LockSupport.unpark(t);
        TimeUnit.SECONDS.sleep(8);
        System.out.println("-------after 8 seconds------");
        LockSupport.unpark(t); // 需要指定线程

    }

}
```
