Concurrent.util常用类

本文详细解析了Java并发控制中的CyclicBarrier和CountDownLatch的使用场景及实现方式,通过具体代码示例展示了如何利用这两种工具类来同步线程间的运行,确保在所有参与线程准备就绪后,再统一执行后续操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CyclicBarrier使用:
想象成一个场景:每一个线程代表一个跑步运动员,当运动员都准备好后,才一起出发,只要有一个没有准备好,大家都等待。
多个阻塞线程等待着一个barrier确定。

package t18;

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

public class UserCyclicBarrier {

	static class Runner implements Runnable{
		
		private String name;
		private CyclicBarrier barrier;

		public Runner(CyclicBarrier barrier,String name){
			this.barrier = barrier;
			this.name = name;
		}
		
		@Override
		public void run() {
			try {
				System.out.println("好了,运动员" + name + "已经准备就绪了~");
				barrier.await();//运动员进入阻塞等待
				Thread.sleep(3000);
				System.out.println(name + "gogogo!!");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}		
		
		public static void main(String[] args) {
			
			CyclicBarrier barrier = new CyclicBarrier(3);
			
			ExecutorService executor = Executors.newFixedThreadPool(3);
			
			executor.submit(new Runner(barrier, "张三"));
			executor.submit(new Runner(barrier, "李四"));
			executor.submit(new Runner(barrier, "王五"));
			
			executor.shutdown();
		}
	}
}

输出结果:

好了,运动员张三已经准备就绪了~
好了,运动员李四已经准备就绪了~
好了,运动员王五已经准备就绪了~
张三gogogo!!
王五gogogo!!
李四gogogo!!

CountDownLacth使用:
他常用于监听某些初始化操作,等初始化执行完毕后,通知主线程继续工作。一个线程等待多个操作完成。

package t18;

import java.util.concurrent.CountDownLatch;

public class UserCountDownLatch {
	
	public static void main(String[] args) {
		
		final CountDownLatch countDown = new CountDownLatch(2);
		
		Thread t1 = new Thread(new Runnable(){

			@Override
			public void run() {
				
				try {
					System.out.println("进入线程t1" + "等待其他线程处理完成...");
					
					countDown.await();
					System.out.println("t1线程继续执行~");
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
			
			
			
		},"t1");
		
		Thread t2 = new Thread(new Runnable(){

			@Override
			public void run() {
				
				try {
					System.out.println("t2线程进行初始化操作(3s)...");
					Thread.sleep(3000);
					System.out.println("t2线程初始化完成~");
					countDown.countDown();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
			
		},"t2");
		
		
		Thread t3 = new Thread(new Runnable(){

			@Override
			public void run() {
				try {
					System.out.println("t3线程进行初始化操作(2s)...");
					Thread.sleep(2000);
					System.out.println("t3线程初始化完成~");
					countDown.countDown();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
		},"t3");
		
		
		t1.start();
		t2.start();
		t3.start();
	}

}

输出结果:

t3线程进行初始化操作(2s)...
进入线程t1等待其他线程处理完成...
t2线程进行初始化操作(3s)...
t3线程初始化完成~
t2线程初始化完成~
t1线程继续执行~

Callable和Future的使用:
Future模式非常适合在处理很耗时很长的业务逻辑时使用,可以减小系统的响应时间,提高系统的吞吐率。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

public class UseFuture implements Callable<String> {
	
	private String para;
	
	public UseFuture(String para){
		this.para = para;
	}
	
	/**
	 * 这里真是的业务逻辑,其执行可能个很慢
	 */
	
	@Override
	public String call() throws Exception {
		
		//模拟执行耗时
		Thread.sleep(5000);
		String result = this.para + "处理完成~";
		
		return result;
	}

	//主控制函数
	public static void main(String args[]) throws InterruptedException, ExecutionException{
		String queryStr = "query";
		//构造FutureTask,并且传入需要真正进行业务逻辑处理的类,该类一定事先Callable接口的类
		FutureTask<String> futureTask1 = new FutureTask<>(new UseFuture(queryStr));
		FutureTask<String> futureTask2 = new FutureTask<>(new UseFuture(queryStr));

		//创建一个固定线程的线程池线程数为2
		ExecutorService executor = Executors.newFixedThreadPool(2);
		
		//这里日交任务future,则开启线程执行RealData的call()方法执行
		Future f1 = executor.submit(futureTask1);
		Future f2 = executor.submit(futureTask2);
		System.out.println("请求完毕");
		System.out.println(f1.get());
		System.out.println(f2.get());
		try {
			System.out.println("处理实际的业务逻辑...");
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.out.println("数据:" + futureTask1.get());
		System.out.println("数据:" + futureTask2.get());
		
		executor.shutdown();
		
	}
}

输出结果:

请求完毕
null
null
处理实际的业务逻辑...
数据:query处理完成~
数据:query处理完成~
<think>嗯,用户遇到了Java中的RejectedExecutionException异常,特别是在使用ScheduledThreadPoolExecutor的时候。我需要先了解这个异常的原因和解决方法。根据引用[2],这个异常通常在线程池无法处理新任务时发生,比如线程池已关闭或达到最大容量。用户提到的是ScheduledThreadPoolExecutor,可能需要具体分析。 首先,异常的原因可能有几种情况。根据引用[2]和[3],线程池关闭或者容量满了。对于ScheduledThreadPoolExecutor来说,可能是在任务提交时,线程池已经被关闭,或者队列已满导致拒绝任务。此外,引用[4]提到在ruoyi项目中,单例模式导致线程池被提前关闭,可能涉及热启动问题。 接下来,解决方法需要针对不同的原因。比如检查线程池是否关闭,调整线程池参数,使用合适的拒绝策略等。引用[3]提到可以调整队列容量,或者使用CallerRunsPolicy来处理任务被拒绝的情况。此外,确保在程序退出时正确关闭线程池也很重要,避免任务被提交到已关闭的池中。 用户可能需要具体的步骤,比如如何配置线程池参数,如何选择合适的拒绝策略,或者如何处理线程池的生命周期。特别是ScheduledThreadPoolExecutor的特定配置,比如核心线程数、队列类型等。另外,引用[4]中的案例提醒要注意单例管理和热部署的问题,可能需要确保线程池在重新部署时被正确重新初始化。 还需要注意,ScheduledThreadPoolExecutor使用的队列是无界的吗?默认情况下,ScheduledThreadPoolExecutor使用的是无界的DelayedWorkQueue,所以通常不会因为队列满而拒绝任务,除非线程池被关闭。所以用户的问题可能更多是线程池被意外关闭导致的。比如在Spring Boot应用中,热部署时可能没有正确重新初始化线程池,导致旧的池被关闭,而新任务仍然提交到旧的实例。 解决方案可能包括:检查线程池状态后再提交任务,使用合适的拒绝策略,或者在应用关闭时正确管理线程池的关闭流程。对于热部署问题,可能需要确保单例的线程池在重新初始化时被正确替换,而不是继续使用已关闭的实例。 总结来说,处理RejectedExecutionException需要从线程池状态、配置参数、拒绝策略以及生命周期管理几个方面入手。用户需要根据具体的情况,比如线程池是否关闭、任务量是否过大等,采取相应的措施。</think>### 解决$java.util.concurrent.RejectedExecutionException$异常的方法(针对$ScheduledThreadPoolExecutor$) #### 1. **异常原因分析** $RejectedExecutionException$的根本原因是任务被提交到无法接收新任务的线程池。具体场景包括: - **线程池已关闭**:调用`shutdown()`后继续提交任务[^2][^4] - **队列容量不足**:线程池使用有界队列且已满,同时最大线程数已耗尽[^3] - **拒绝策略触发**:显式设置的拒绝策略(如$AbortPolicy$)主动拒绝任务 #### 2. **解决方案** ##### (1) 检查线程池状态 提交任务前验证线程池是否关闭: ```java if (!executor.isShutdown()) { executor.schedule(task, delay, TimeUnit.MILLISECONDS); } ``` ##### (2) 调整线程池配置 根据业务需求优化核心参数: - **增大核心线程数**:`setCorePoolSize()` - **使用无界队列**(慎用):默认$ScheduledThreadPoolExecutor$使用无界$DelayedWorkQueue$,但需警惕内存溢出风险 - **动态扩容**:通过自定义线程池工厂实现按需扩展 ##### (3) 设置合理拒绝策略 通过构造函数指定策略: ```java new ScheduledThreadPoolExecutor( corePoolSize, new ThreadPoolExecutor.CallerRunsPolicy() // 由提交任务的线程直接执行 ); ``` 常用策略对比: | 策略名称 | 行为描述 | |--------------------|---------------------------------| | `AbortPolicy` | 直接抛出异常(默认) | | `CallerRunsPolicy` | 由调用线程执行任务 | | `DiscardPolicy` | 静默丢弃任务 | ##### (4) 生命周期管理 确保应用关闭时**有序终止线程池**: ```java executor.shutdown(); if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { executor.shutdownNow(); } ``` ##### (5) 热部署场景特别处理 在Spring Boot等框架中,若使用单例线程池(如ruoyi项目的$AsyncManager$),需确保热重启后**重新初始化线程池**。可通过`@DependsOn`注解或重写Bean销毁逻辑实现。 #### 3. **验证方法** - 监控线程池状态:通过`getActiveCount()`/`getQueue().size()`等API实时观测 - 添加日志跟踪:在任务提交/执行前后记录线程池状态 - 压力测试:使用JMeter等工具模拟高并发场景
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值