java开发中常用的几种多线程

一、CompletableFuture

1.使用场景
在一次请求多个接口时,需要获取多个接口的返回数据,并进行组装结果集返回。

2.示例代码

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompletableFutureExample {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Task 1");
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Task 2");
		CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "Task 3");
		
		// 使用allOf和anyOf来等待所有任务完成或任意一个任务完成。
        // 等待所有任务完成
        CompletableFuture.allOf(future1, future2, future3).join();

        // 或者等待任意一个任务完成
        CompletableFuture<Void> anyOf = CompletableFuture.anyOf(future1, future2, future3);
        if (anyOf.isDone()) {
    
    
            System.out.println("At least one task is done.");
        }
    }
}

二、CountDownLatch

1.使用场景
在某些业务场景中,程序执行需要等待某个条件完成后才能继续执行后续的操作。典型的应用为并行计算:当某个处理的运算量很大时,可以将该运算任务拆分成多个子任务,等待所有的子任务都完成之后,父任务再拿到所有子任务的运算结果进行汇总。

2.代码示例

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

public class CountDownLatchExample {
    
    

    public static void main(String[] args) {
    
    
		ExecutorService exec = Executors.newCachedThreadPool();
        final int numThreads = 3;
        CountDownLatch latch = new CountDownLatch(numThreads);

        for (int i = 0; i < numThreads; i++) {
    
    
        //这里也可以采用异步的方式进行调用,如@Async注解对应的方法
           exec.execute(() -> {
    
    
			try {
    
    
					test(threadNum);
				} catch (InterruptedException e) {
    
    
					e.printStackTrace();
				}finally {
    
    
					//每个线程执行结束后,计数器减1
					countDownLatch.countDown();
				}
			});
        }
	        // 主线程等待所有子线程完成
	        try {
    
    
	            System.out.println("主线程正在等待其他线程完成...");
	            // 等待所有的线程执行完毕
	            latch.await(); 
	        } catch (InterruptedException e) {
    
    
	            e.printStackTrace();
	        }
    }

    private static void doWork(CountDownLatch latch) throws InterruptedException {
    
    
        System.out.println(Thread.currentThread().getName() + " 开始工作...");
        Thread.sleep(2000); // 模拟耗时操作
        System.out.println(Thread.currentThread().getName() + " 工作完成。");
		// 完成工作后计数器每次减1
        latch.countDown(); 
    }
}

CountDownLatch是Java并发包java.util.concurrent中的一种同步辅助类,它允许一个或多个线程等待其他线程完成操作。
CountDownLatch的计数器被初始化为一个特定的值,当计数器到达零时,所有因等待计数器变为零而被阻塞的线程将被释放。

三、Semaphore

1.使用场景
Semaphore常用于仅能提供有限访问的资源,比如:数据库连接数。

2.代码示例

import java.util.concurrent.Semaphore;
/**
下面是一个使用Semaphore的示例,我们将模拟一个停车场场景,停车场只有5个停车位,
当有车辆试图进入时,如果没有空位,则车辆需要等待,直到有车位空出。
*/
public class SemaphoreExample {
    
    

    public static void main(String[] args) {
    
    
        final int NUM_PARKING_SPOTS = 5;
        // 初始化5个许可
        Semaphore parkingLot = new Semaphore(NUM_PARKING_SPOTS, true); 

        for (int i = 0; i < 10; i++) {
    
    
            new Thread(() -> {
    
    
                try {
    
    
                    parkCar(parkingLot);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }).start();
        }
    }

    private static void parkCar(Semaphore parkingLot) throws InterruptedException {
    
    
        System.out.println(Thread.currentThread().getName() + " 正在寻找停车位...");
        // 尝试获取一个停车许可
        parkingLot.acquire(); 
        System.out.println(Thread.currentThread().getName() + " 找到了停车位。");

        try {
    
    
			// 模拟停车时间
            Thread.sleep(2000); 
            System.out.println(Thread.currentThread().getName() + " 停车结束。");
        } finally {
    
    
        	// 释放停车许可
            parkingLot.release(); 
            System.out.println(Thread.currentThread().getName() + " 离开了停车场。");
        }
    }
}

Semaphore是Java并发包java.util.concurrent中用于控制对共享资源访问的同步工具类。它通过一组许可(permits)来控制对资源的访问,当许可数量大于0时,线程可以获取许可并访问资源,否则线程将被阻塞直到获得许可。

四、CyclicBarrier

1.使用场景
可以用于多线程计算数据,最后合并计算结果的场景

2.代码示例

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.BrokenBarrierException;
/**
下面是一个使用CyclicBarrier的示例,我们将模拟一个比赛场景,其中多个赛跑者(线程)在起跑线上等待裁判的发令枪声,所有赛跑者同时开始跑步。
*/
public class CyclicBarrierExample {
    
    

    public static void main(String[] args) {
    
    
        final int numberOfRunners = 5;
        CyclicBarrier barrier = new CyclicBarrier(numberOfRunners, () -> {
    
    
            System.out.println("所有赛跑者已经准备就绪,比赛开始!");
        });

        for (int i = 0; i < numberOfRunners; i++) {
    
    
            new Thread(() -> {
    
    
                try {
    
    
                    prepareRunner(barrier);
                } catch (InterruptedException | BrokenBarrierException e) {
    
    
                    e.printStackTrace();
                }
            }).start();
        }
    }

    private static void prepareRunner(CyclicBarrier barrier) throws InterruptedException, BrokenBarrierException {
    
    
        System.out.println(Thread.currentThread().getName() + " 正在准备...");
        // 模拟准备时间
        Thread.sleep((long) (Math.random() * 1000)); 
        System.out.println(Thread.currentThread().getName() + " 准备完毕,等待发令枪响。");

		// 等待所有赛跑者准备就绪
        barrier.await(); 

        System.out.println(Thread.currentThread().getName() + " 开始跑步!");
        // 模拟跑步时间
        Thread.sleep((long) (Math.random() * 1000)); 
        System.out.println(Thread.currentThread().getName() + " 到达终点!");
    }
}

CyclicBarrier是Java并发包java.util.concurrent中的一个同步辅助类,它允许一组线程相互等待,直到到达某个公共屏障点(barrier)。当所有参与的线程都到达了这个屏障点,所有线程将被释放并继续执行。CyclicBarrier的一个关键特性是它是可重用的,即一旦释放了所有线程,它可以再次被重置以供下一轮循环使用。

四、CyclicBarrier与CountDownLatch的区别

  1. CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法进行重置,并且可以循环使用
  2. CountDownLatch主要实现1个或n个线程需要等待其他线程完成某项操作之后,才能继续往下执行,描述的是1个或n个线程等待其他线程的关系。而CyclicBarrier主要实现了多个线程之间相互等待,直到所有的线程都满足了条件之后,才能继续执行后
    续的操作,描述的是各个线程内部相互等待的关系。
  3. CyclicBarrier能够处理更复杂的场景,如果计算发生错误,可以重置计数器让线程重新执行一次。
  4. CyclicBarrier中提供了很多有用的方法,比如:可以通过getNumberWaiting()方法获取阻塞的线程数量,通过isBroken()方法判断阻塞的线程是否被中断。

猜你喜欢

转载自blog.csdn.net/weixin_45235440/article/details/140357523