java并发之Countdownlatch和Semaphore

q1871901600 发布于 2024-11-14 28 次阅读


CountDownLatch和Semaphore的区别和底层原理

CountDownLatch表示计数器,可以给CountDownLatch设置一个数字,一个线程调用CountDownLatch的await()将会阻塞,其他线程可以调用CountDownLatch的countDown0方法来对CountDownLatch中的数字减一,当数字被减成0后,所有awaft的线程都将被赖醒。

对应的底层原理就是,调用await()方法的线程会利用AQS排队,一旦数字被减为0,则会将AQS中排队的线程依次唤醒。

Semaphore表示信号量,可以设置许可的个数,表示同时允许最多多少个线程使用该信号量,通过acquire()来获取许可,如果没有许可可用则线程阻塞,并通过AQS来排队,可以通过release()方法来释放许可,当某个线程释放了某个许可后,会从AQS中正在排队的第一个线程

开始依次唤醒,直到没有空闲许可。

Countdownlatch

场景1 让多个线程等待:模拟并发,让并发线程一起执行

场景2 让单个线程等待:多个线程(任务)完成后,进行汇总合并

ExecutorService service = Executors.newFixedThreadPool(3);
final CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                System.out.println("子线程" + Thread.currentThread().getName() + "开始执行");
                Thread.sleep((long) (Math.random() * 10000));
                System.out.println("子线程"+Thread.currentThread().getName()+"执行完成");
                latch.countDown();//当前线程调用此方法,则计数减一
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };
    service.execute(runnable);
}

try {
    System.out.println("主线程"+Thread.currentThread().getName()+"等待子线程执行完成...");
    latch.await();//阻塞当前线程,直到计数器的值为0
    System.out.println("主线程"+Thread.currentThread().getName()+"开始执行...");
} catch (InterruptedException e) {
    e.printStackTrace();
}
CountDownLatch与CyclicBarrier
CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:

CountDownLatch一般用于一个或多个线程,等待其他线程执行完任务后,再才执行
CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行
另外,CountDownLatch是减计数,计数减为0后不能重用;而CyclicBarrier是加计数,可置0后复用。

Semaphore

经常用于限制获取某种资源的线程数量。

package com.thread.semaphore;

import java.util.concurrent.Semaphore;

class MyThread extends Thread {
    private Semaphore semaphore;
    
    public MyThread(String name, Semaphore semaphore) {
        super(name);
        this.semaphore = semaphore;
    }
    
    public void run() {        
        int count = 3;
        System.out.println(Thread.currentThread().getName() + " trying to acquire");
        try {
            semaphore.acquire(count);
            System.out.println(Thread.currentThread().getName() + " acquire successfully");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release(count);
            System.out.println(Thread.currentThread().getName() + " release successfully");
        }
    }
}

public class SemaphoreDemo {
    public final static int SEM_SIZE = 10;
    
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(SEM_SIZE);
        MyThread t1 = new MyThread("t1", semaphore);
        MyThread t2 = new MyThread("t2", semaphore);
        t1.start();
        t2.start();
        int permits = 5;
        System.out.println(Thread.currentThread().getName() + " trying to acquire");
        try {
            semaphore.acquire(permits);
            System.out.println(Thread.currentThread().getName() + " acquire successfully");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release();
            System.out.println(Thread.currentThread().getName() + " release successfully");
        }
        
                
    }
}

说明:

首先,生成一个信号量,信号量有10个许可,然后,main,t1,t2三个线程获取许可运行

首先,main线程执行acquire操作,并且成功获得许可,之后t1线程执行acquire操作,成功获得许可,之后t2执行acquire操作,由于此时许可数量不够,t2线程将会阻塞,直到许可可用。之后t1线程释放许可,main线程释放许可,此时的许可数量可以满足t2线程的要求,所以,此时t2线程会成功获得许可运行,t2运行完成后释放许可。

一个会写python的Java工程师
最后更新于 2024-11-14