多线程同步器

java.util.concurrent包中提供了3个很实用的工具类帮我们进行复杂的线程同步控制,分别是减少计数器CountDownLatch、循环栅栏CyclicBarrier和信号灯Semaphore

减少计数器 CountDownLatch

CountDownLatch内部维护了一个计数器,调用countDown方法可以使计数器的值减少1,子线程可以使用await方法阻塞等待,直到计数器的值小于0。我们直接看一个例子。

package com.gacfox.demo;

import java.util.concurrent.CountDownLatch;

public class MyThread implements Runnable {

    private CountDownLatch countDownLatch;

    public MyThread(CountDownLatch latch) {
        this.countDownLatch = latch;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        countDownLatch.countDown();
    }
}
package com.gacfox.demo;

import java.util.concurrent.CountDownLatch;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(3);
        new Thread(new MyThread(countDownLatch)).start();
        new Thread(new MyThread(countDownLatch)).start();
        new Thread(new MyThread(countDownLatch)).start();
        countDownLatch.await();
        System.out.println("计数器耗尽!");
    }
}

代码中,我们的主线程会在CountDownLatch小于0前一直阻塞等待,子线程不断将计数器值减小,最终触发主线程await()返回。

循环栅栏 CyclicBarrier

循环栅栏内部维护了一个栅栏值,当多个子线程await()到循环栅栏上时,如果await()的线程数达到栅栏值,这些线程的await()会一起返回。

package com.gacfox.demo;

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

public class MyThread implements Runnable {

    private final CyclicBarrier cyclicBarrier;

    public MyThread(CyclicBarrier barrier) {
        this.cyclicBarrier = barrier;
    }

    @Override
    public void run() {
        try {
            this.cyclicBarrier.await();
        } catch (InterruptedException | BrokenBarrierException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程" + Thread.currentThread().getId() + "开始执行");
    }
}
package com.gacfox.demo;

import java.util.concurrent.CyclicBarrier;

public class Main {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
        new Thread(new MyThread(cyclicBarrier)).start();
        new Thread(new MyThread(cyclicBarrier)).start();
        new Thread(new MyThread(cyclicBarrier)).start();
    }
}

代码中,我们创建了3个线程对循环栅栏进行await()操作,最终达到栅栏值所有线程返回。

信号灯 Semaphore

信号灯内部维护了一些令牌,子线程可以从信号灯中“抢”令牌,令牌全部发出去后,未获得到令牌的线程就只能阻塞等待了,有子线程释放令牌后,未获取令牌的子线程才可以继续执行。

package com.gacfox.demo;

import java.util.concurrent.Semaphore;

public class MyThread implements Runnable {
    private final Semaphore semaphore;

    public MyThread(Semaphore semaphore) {
        this.semaphore = semaphore;
    }

    @Override
    public void run() {
        try {
            this.semaphore.acquire();
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程" + Thread.currentThread().getId() + "开始执行");
        this.semaphore.release();
    }
}
package com.gacfox.demo;

import java.util.concurrent.Semaphore;

public class Main {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);
        new Thread(new MyThread(semaphore)).start();
        new Thread(new MyThread(semaphore)).start();
        new Thread(new MyThread(semaphore)).start();
        new Thread(new MyThread(semaphore)).start();
        new Thread(new MyThread(semaphore)).start();
    }
}

上面代码中,我们创建的信号灯中有3个令牌,但有5个线程在获取。执行后,我们会观察到3个线程先执行完,他们释放令牌后,剩余的2个线程才会继续执行。注意这里我们的线程执行完后不要忘记释放令牌,否则后面的线程就永远获取不到令牌了。此外,线程也可以一次获取多个令牌,具体API可以参考JDK文档,这里及不展开介绍了。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap