Узнайте о четырех классах инструментов синхронизации в одной статье

Java

CountDownLatch

объяснять:

CountDownLatch эквивалентен защелке, и на защелку навешивается N замков. Дверь откроется, только если N откроет все замки. Как понять это? Позвольте мне привести пример гонки.В гонке вы должны дождаться, пока все бегуны будут готовы, прежде чем рефери сможет развернуть гаечный ключ. Спортсмены могут начать бегать. В CountDownLatch есть два основных метода: один из них заключается в том, что await() блокирует текущий поток и блокирует текущий поток, что эквивалентно ожиданию судьи в начальной точке, ожидая готовности игроков, а затем вызывает метод countDown. сообщить судье, что он готов.Когда все готовы, судья разворачивает пистолет.

Код:

public class TestCountDownLatch {
    public static void main(String[] args) {
        // 需要等待两个线程,所以传入参数为2
        CountDownLatch latch = new CountDownLatch(2);
        // 该线程运行1秒
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("1号选手准备就绪!用时1秒!");
                latch.countDown();
            }
        }).start();
        
        // 该线程运行3秒
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("2号选手准备就绪!用时3秒!");
                latch.countDown();
            }
        }).start();
        
        try {
            System.out.println("请1号选手和2号选手各就各位!");
            // 主线程在此等待两个线程执行完毕之后继续执行
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 两个线程执行完毕后,主线程恢复运行
        System.out.println("裁判发枪,1号选手和2号选手开跑!");
    }
}

результат операции:

请1号选手和2号选手各就各位!
1号选手准备就绪!用时1秒!
2号选手准备就绪!用时3秒!
裁判发枪,1号选手和2号选手开跑!

Что если убрать эффект CountDownLatch? Текущий результат станет следующим:

请1号选手和2号选手各就各位!
裁判发枪,1号选手和2号选手开跑!
1号选手准备就绪!用时1秒!
2号选手准备就绪!用时3秒!

Судья развернет командный пистолет, когда игрок не готов, что испортило.
На самом деле, одно из самых простых применений CountDownLatch — вычисление времени завершения многопоточности. Как и в предыдущем примере, два потока, выполняемые параллельно, заняли в общей сложности 3 секунды.

CyclicBarrier

объяснять:

CyclicBarrier похож на забор, блокирующий каждый поток. Cyclic в переводе с английского означает «цикл», что указывает на возможность повторного использования инструмента. Параметр конструкции CyclicBarrier(N) указывает на то, что существует несколько потоков, которые должны ожидать друг друга. Это эквивалентно тому, что N игроков договариваются провести несколько гонок, и они должны ждать друг друга в начальной точке после каждой гонки. Читатели могут сразу же задаться вопросом, не то же ли это, что и CountDownLatch? Разные. Поскольку CountDownLatch является рефери, ожидающим игроков, потоком, вызывающим метод await(), и потоками, ожидающими вызова метода countDown(). CyclicBarrier — это игрок, ожидающий игрока.Потоки, которые вызывают метод await(), ждут друг друга и ждут, пока другие потоки будут хорошо работать, прежде чем начать следующий раунд выполнения.

Возьмем пример, когда соревнуются два игрока, и всего три раунда.

Код:

public class TestCyclicBarrier {
    // 1号选手跑的轮数
    public static int countA = 1;
    // 2号选手跑的轮数
    public static int countB = 1;
    public static void main(String[] args) {
        // 填入2,代表2个线程互相等待
        CyclicBarrier barrier = new CyclicBarrier(2);

        new Thread(new Runnable() {
            @Override
            public void run() {
                // 一共跑三轮
                for (int i = 0; i < 3; i++) {
                    System.out.println("1号选手开始跑!当前第" + countA++ + "轮比赛!");
                    // 1号选手跑得慢,每次跑三秒
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    try {
                        System.out.println("1号选手抵达终点!");
                        // 调用等待方法,在此等待其他选手
                        barrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                // 一共等待三轮
                for (int i = 0; i < 3; i++) {
                    System.out.println("2号选手开始跑!当前第" + countB++ + "轮比赛!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    try {
                        System.out.println("2号选手抵达终点!");
                        // 调用等待方法,在此等待其他选手
                        barrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

}

результат операции:

1号选手开始跑!当前第1轮比赛!
2号选手开始跑!当前第1轮比赛!
2号选手抵达终点!
1号选手抵达终点!
1号选手开始跑!当前第2轮比赛!
2号选手开始跑!当前第2轮比赛!
2号选手抵达终点!
1号选手抵达终点!
1号选手开始跑!当前第3轮比赛!
2号选手开始跑!当前第3轮比赛!
2号选手抵达终点!
1号选手抵达终点!

Игроки №1 и №2 возвращаются на стартовую линию в каждом раунде, чтобы дождаться друг друга перед началом следующего раунда.
Что делать, если CyclicBarrier не добавлен?

1号选手开始跑!当前第1轮比赛!
2号选手开始跑!当前第1轮比赛!
2号选手抵达终点!
2号选手开始跑!当前第2轮比赛!
2号选手抵达终点!
2号选手开始跑!当前第3轮比赛!
1号选手抵达终点!
1号选手开始跑!当前第2轮比赛!
2号选手抵达终点!
1号选手抵达终点!
1号选手开始跑!当前第3轮比赛!
1号选手抵达终点!

В это время игрок №2 пробежал сразу три раунда, не дожидаясь игрока №1.

Semaphore

Semaphore буквально означает семафор на английском языке. Его рабочий механизм заключается в том, что если каждый поток хочет получить шанс запуститься, он должен получить семафор. Метод Acquire() блокирует получение семафора, а метод release() освобождает семафор. Например, предположим, что мы идем в Дисней, чтобы поиграть, но Дисней обеспокоен тем, что если будет слишком много туристов, это повлияет на игровой опыт каждого, поэтому в нем оговаривается, что в час можно продавать только два билета. Таким образом можно контролировать количество туристов в парке развлечений.

Код:

public class TestSemaphore {

    public static void main(String[] args) {

        Semaphore semaphore = new Semaphore(0);
        System.out.println("顾客在售票处等候中");
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (; ; ) {
                    try {
                        Thread.sleep(500);
                        // 等待出票
                        semaphore.acquire();
                        System.out.println("顾客拿到门票入场!");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {

                for (int i = 0; i < 3; i++) {
                    try {
                        // 等待一小时再发门票
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    // 一次性发出两张门票
                    System.out.println("售票处第" + (i + 1) + "小时售出两张票!");
                    semaphore.release();
                    semaphore.release();
                }
            }
        }).start();

        System.out.println("售票处开始售票!");
    }
}

результат операции:

顾客在售票处等候中...
售票处开始售票!
售票处第1小时售出两张票!
顾客拿到门票入场!
顾客拿到门票入场!
售票处第2小时售出两张票!
顾客拿到门票入场!
顾客拿到门票入场!
售票处第3小时售出两张票!
顾客拿到门票入场!
顾客拿到门票入场!

Exchanger

объяснять:

Exchanger предоставляет точки синхронизации для двух потоков для обмена данными друг с другом. Exchanger немного похож на CyclicBarrier с двумя потоками, потоки ждут друг друга, разница в том, что у Exchanger больше операций обмена. Например, в прошлом, играя в онлайн-игры, покупатели и продавцы должны были идти в одно и то же место на карте, чтобы совершать сделки лицом к лицу, оплачивая деньги одной рукой и оборудование другой.

Код:

public class TestExchanger {
    public static void main(String[] args) {
        Exchanger<String> exchanger = new Exchanger<>();
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                String weapon = "装备";
                System.out.println("我是卖家,我带着" + weapon + "过来了!");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("卖家到达地图上交易地点");
                try {
                    System.out.println("我是卖家,换回了" + exchanger.exchange(weapon));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                String money = "一万游戏币";
                System.out.println("我是买家,我带着" + money + "过来了");
                try {
                    Thread.sleep(4000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("买家到达地图上交易地点");
                try {
                    System.out.println("我是买家,换回了" + exchanger.exchange(money));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();
    }

результат операции:

我是卖家,我带着装备过来了!
我是买家,我带着一万游戏币过来了
卖家达到交易地点
买家到达交易地点
我是买家,换回了装备
我是卖家,换回了一万游戏币

Если есть какие-либо ошибки, пожалуйста, не стесняйтесь комментировать и исправлять.

Перейдите на мой личный блог vc2x.com