Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

  • 1、前言
  • 2、CountDownLatch理解和使用
  • 3、CyclicBarrier理解和使用
  • 4、Semaphore理解和使用

1、前言

由于对CountDownLatch和CyclicBarrier的功能容易混淆不清,于是特地把这两个类拉出来进行对比。顺带也把Semaphore拿过来一起进行介绍。希望本篇博客能够帮助读者能够清晰的了解到这三个同步组件。

2、CountDownLatch理解和使用

CountDownLatch:该类的功能可以分为两部分任务;一部分负责阻塞当前线程,另一部分负责唤醒被阻塞的线程。下面我们分别来看一下。
1、调用await()方法的负责阻塞当前线程,阻塞线程相互之间也不会因为await()方法造成影响。
2、另一部分任务是调用countDown(),改变state的值,当state值为0时,所有阻塞在await()方法的线程都会被唤醒。
这后面还有两点需要注意的是:
1、当state为0后,随后所有调用await()方法的线程都不会阻塞。
2、countDown()改变state值,状态是无法被恢复为初始状态。
下面一段代码展示CountDownLatch是如何使用的。
在这里countDown()扮演的是司机的角色,await()扮演的是乘客的角色。乘客不断来到车边想要上车,但是司机没有开门,乘客都只能等待着【await()】,随后司机开门【countDown()】,所有的乘客都可以上车;随后到来的乘客就无需等待可以直接上车了。当然这里CountDownLatch可以更多,比如我们再加一个乘务员的角色,要保证乘务员能够卖票后【countDown()】,乘客才能上车。

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class CountDownLatchClient {
    static Integer driverCount = 1;
    static Integer passengerCount = 40;
    static CountDownLatch cdl = new CountDownLatch(driverCount);

    public static void main(String[] args) {
        for (int i = 0; i < passengerCount; i++) {
            Thread t = new Thread(new Passenger(), "passenger-" + i);
            t.start();
        }
        for (int i = 0; i < driverCount; i++) {
            Thread t = new Thread(new Driver(), "driver-" + i);
            t.start();
        }
    }

    static class Driver implements Runnable {
        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
                cdl.countDown();
                System.out.println("司机【" + Thread.currentThread().getName() + "】已开门");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

    static class Passenger implements Runnable {
        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
                System.out.println("乘客【" + Thread.currentThread().getName() + "】准备上车,等待【" + cdl.getCount() + "】位司机开门");
                cdl.await();
                System.out.println("乘客【" + Thread.currentThread().getName() + "】已上车");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3、CyclicBarrier理解和使用

CyclicBarrier:这个同步组件只有一个任务,就是阻塞。当阻塞数量达到指定数量的一组后。这一组所有线程全部被唤醒,执行后续代码;并初始化CyclicBarrier。
下面一段代码展示CyclicBarrier的使用。
在这里线程表示的是乘客,每个乘客都可以上车【await()】,在汽车未坐满之前,所有乘客只能等待。坐满之后,汽车出发;所有乘客前往目的地。汽车站又来一部汽车,其他乘客可以继续上车,可以不断重复载人,开车。

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;

public class CycleBarrierClient {

    static Integer passengerCount = 50;
    static Integer carLoad = 20;
    static CyclicBarrier cb = new CyclicBarrier(carLoad);

    public static void main(String[] args) {
        for (int i = 0; i < passengerCount; i++) {
            Thread t = new Thread(new Passenger(), "passenger-" + i);
            t.start();
        }
    }
    static class Passenger implements Runnable {
        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
                System.out.println("乘客【" + Thread.currentThread().getName() + "】已上车,等待出发,已有【" + (cb.getNumberWaiting() + 1) + "】乘客等待中...");
                cb.await();
                System.out.println("被告知,乘客已坐满,汽车出发");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
}

4、Semaphore理解和使用

Semaphore:信号量,线程先获取信号量【acquire()】,若能获取到,就继续执行后续代码;若不能获取到,就阻塞在当前acquire()方法上。当已获取信号量的线程释放信号量后,阻塞在acquire()方法上的线程可以获得信号量。
信号量【Semaphore】看来是一种资源,每个线程都尝试去占用,当资源全部被占用后,其他还未获取到资源的线程只能等待,等待前面得到资源的线程释放资源给后续线程使用。
仔细想来Semaphore其实就是一种共享锁,指定能够被共享的总数量。
下面一段代码展示Semaphore的使用。
这里商家有5台游戏机,但是有40个人想去玩游戏,5台游戏机被占满后,其他玩家只能等待。等待某个正在游戏的玩家结束游戏,把游戏机让出来。其他玩家才能到游戏机上进行游戏。

import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreClient {
    static Integer gameMachineCount = 5;
    static Integer playerCount = 40;
    static Semaphore gameMachines = new Semaphore(gameMachineCount);

    public static void main(String[] args) {
        for (int i = 0; i < playerCount; i++) {
            Thread t = new Thread(new Player(), "Player-" + i);
            t.start();
        }
    }

    static class Player implements Runnable {
        @Override
        public void run() {
            try {
                System.out.println("玩家" + Thread.currentThread().getName() + "开始排队,剩余【" + gameMachines.availablePermits() + "】台游戏机");
                gameMachines.acquire();
                System.out.println("玩家" + Thread.currentThread().getName() + "开始游戏,剩余【" + gameMachines.availablePermits() + "】台游戏机");
                TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
                gameMachines.release();
                System.out.println("玩家" + Thread.currentThread().getName() + "结束游戏,剩余【" + gameMachines.availablePermits() + "】台游戏机");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

你可能感兴趣的:(Java并发编程)