JUC 中三个超好用的辅助类

发布时间 2023-08-10 13:07:50作者: 劣技砖猿

CountDownLatch

简介

CountDownLatch 是基于计数器的原理实现的, 它内部维护了一个整型的计数器。创建 CountDownLatch 对象时, 需要指定一个初始计数值, 该计数值表示需要等待的线程数量,使用其 await() 方法让其等待。每当一个线程完成了其任务, 可以调用 CountDownLatch 的 countDown() 方法, 计数器的值就会减一。当计数器的值变成 0 时, 等待的线程就会被唤醒, 继续执行它们的任务。

使用示例

public static void mian() throw InterruptedException{
	//创建倒计时门闩对象
	CountDownLatch latch = new CountDownLatch(5);
	for(int i = 1 ; i <= 5 ; i++){
		new Thread(()->{
			try {
				//模拟耗时操作
                Thread.sleep(1000);
            }catch (Exception e){
                e.printStackTrace();
            }
			System.out.println( i + "号线程执行完毕。");
			//计数器减一
			latch.countDown();
		} , i + "");
	}
	//主线程等待其他五个线程执行完后才继续执行
	downLatch.await();

	//await() 的重载方法,当计数器计数为0或者等待时间到达 5 秒,主线程会被唤醒
	//downLatch.await( 5 , TimeUnit.SECONDS );

	System.out.println( "其他线程全部执行完毕,主线程继续运行。");
}

CyclicBarrier

简介

CyclicBarrier 可以让一组线程互相等待,直到所有线程都到达某个公共屏障点(Barrier point)。它是一个循环栅栏,因为线程可以在屏障点再次达到后重新进行等待。CyclicBarrier 的使用需要指定两个参数:参与的线程数和屏障触发时的处理程序。当指定数量的线程到达屏障点时,所有线程都会被唤醒,并且可以执行一些共享的操作。在 CyclicBarrier 中,线程在等待时会释放所占用的资源,因此它适用于在多线程环境下同步多个线程完成任务的情况。它也常用于实现并行计算和并发测试的场景。同时,CyclicBarrier 是可以重复使用的,只需要调用其 reset() 方法,便可将屏障恢复至初始状态。

使用示例

public static void main(String[] args) {  
        // 创建一个CyclicBarrier,指定参与的线程数和屏障触发时的处理程序  
        CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() {  
            @Override  
            public void run() {  
                System.out.println("所有线程都已到达屏障点,开始执行任务...");
                // 在这里执行需要所有线程共同完成的任务  
            }  
        });  
  
        // 创建3个线程,分别执行不同的任务  
        Thread thread1 = new Thread(()->{
			try {
				//模拟耗时操作
				Thread.sleep(1000);
				//线程到达屏障点,等待其他线程
				System.out.println( "1号线程到达屏障点。");
                barrier.await();
				//await() 的重载方法,当所有线程到达屏障点或者等待时间到达 5 秒,线程会被唤醒
				//barrier.await( 5 , TimeUnit.SECONDS);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
		}, "Thread-1");  
        Thread thread2 = new Thread(()->{
			try {
				Thread.sleep(1000);
				System.out.println( "2号线程到达屏障点。");
                barrier.await();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
		}, "Thread-2");  
        Thread thread3 = new Thread(()->{
			try {
				Thread.sleep(1000);
				System.out.println( "3号线程到达屏障点。");
                barrier.await();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
		}, "Thread-3");

        // 启动线程
        thread1.start();
        thread2.start();
        thread3.start();
		
		//将屏障回复至初始状态,之后便可以再次使用
		//barrier.reset()
    }

Semaphore

简介

Semaphore 用于控制或限制对资源的访问。它是一个计数信号量,可以用来保护一段代码或共享资源,确保同时只有一个线程可以访问它,就类似抢车位。Semaphore 的使用需要创建一个 Semaphore 对象,并使用它的 acquire() 和 release() 方法来控制资源的访问。当一个线程需要访问资源时,它会调用 acquire() 方法来获取一个许可。如果许可可用,线程将获取许可并继续执行。如果许可不可用,线程将被阻塞,直到有可用的许可。当线程完成对资源的访问后,它会调用 release() 方法来释放许可,以便其他线程可以获取许可并访问资源。

使用示例

public static void main(String[] args) {
	// 创建一个初始许可数为2的Semaphore,代表有两个车位
	Semaphore semaphore = new Semaphore(2);
	//创建4个线程,代表4辆汽车
    for(int i = 1; i <= 4;i++){
		Thread thread = new Thread(() -> {
			try {
				// 获取一个许可,代表抢到车位
				semaphore.acquire();
				System.out.println(i + "号汽车抢到车位");
				// 模拟对资源的访问,代表停车
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally{
				// 释放许可,代表离开车位
				semaphore.release();
				System.out.println(i + "号汽车离开车位");
			}
		}).start();
	}
}