Java线程池的使用

在开发过程中,我们有时需要频繁创建多个线程进行运算的时候,每次都new出一个新的Thread对象并不能有效地利用资源,同时可能造成由于线程缺乏统一管理,无限制创建新线程,占用过多资源导致线程死锁问题。在这样的情况下我们就可以使用Java的线程池来管理、调度线程。

线程池的的优点在于三点:

一,能够复用存在的线程,减少创建、销毁线程的操作,减少进行对应操作的开销;

二、能够有效控制并发数量,提高系统资源利用率,避免过多资源竞争,避免拥堵;

三、提供定式执行、定期执行、单线程、并发数控制等。

Java提供的线程池有4种:newFixedThreadPool、newScheduledThradPool、newCachedThreadPool、newSingleThreadExecutor。

创建线程池,可以通过Executors的对应方法来实现:

newFixedThreadPool:创建一个指定长度的线程池,线程最大并发数量不超过线程池长度,如果超出则等待其他线程执行完毕。

public class ExecutorDemo {
	
	public static void main(String[] args) {
		ExecutorService executorService=Executors.newFixedThreadPool(4);
		for(int i=0;i<8;i++){
			executorService.submit(new ExecRunnalbe());
		}
		executorService.shutdown();
	}
	
}

Executors.newFixedThreadPool(4)创建了一个长度为4的线程池,并且返回一个ExecutorService对象,用于对线程池的调度,通过submit方法向线程池的线程注入Runnable或者Callable。执行之后的结果:

pool-1-thread-2正在运行
pool-1-thread-1正在运行
pool-1-thread-3正在运行
pool-1-thread-2正在运行
pool-1-thread-1正在运行
pool-1-thread-1正在运行
pool-1-thread-2正在运行
pool-1-thread-4正在运行

可以看出,就算当前线程需要执行的线程有8个,实际并发执行的线程只有4个,线程空闲之后会被复用。

newScheduledThread:创建一个指定长度的线程池,允许周期性执行,支持定时执行。

public class ExecutorDemo {
	public static void main(String[] args) {
		ScheduledExecutorService schedService=Executors.newScheduledThreadPool(3);
		schedService.scheduleAtFixedRate(new ExecRunnalbe(),1,3,TimeUnit.SECONDS);
	}
	
}

Executors.newScheduledThreadPool(3)方法创建了一个长度为3的线程池,并且返回一个ScheduledExecutorService对象,可以通过该对象注入任务Runnable。scheduleAtFixedRate方法声明如下:

•	scheduleAtFixedRate
•	ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
•	                                       long initialDelay,
•	                                       long period,
                                       TimeUnit unit)

可以看出,第一个参数为需要执行的Runnable,第二个参数是延迟时间,第三个参数为任务周期,第四个参数为时间单位。

运行之后程序会一直执行Runnable的任务,输出结果如下:

pool-1-thread-1正在运行
pool-1-thread-1正在运行
pool-1-thread-2正在运行
pool-1-thread-2正在运行
pool-1-thread-3正在运行
pool-1-thread-1正在运行
pool-1-thread-2正在运行
pool-1-thread-3正在运行
....

上述代码中创建的线程池长度为3,所以线程池中始终最多只有三个线程运行,并且在执行shutdown方法之前(上面的代码没有),程序会周期性地执行线程。

newCacheThreadPool:创建一个可缓冲线程池,如果存在空闲的线程,复用空闲线程,否则创建新线程。

public class ExecutorDemo {

	
	public static void main(String[] args) {
		ExecutorService cacheService=Executors.newCachedThreadPool();
		for(int i=0;i<8;i++){
			if(i==6){
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			cacheService.submit(new ExecRunnalbe());
		}
		cacheService.shutdown();
		
	}
	
}

上述程序中,当i不等于6之前,由于不断需要新线程,所以程线程池会不断创建线程来满足需求,当i等于6时,由于主线程休眠1秒,子线程执行完毕之后空闲,休眠结束之后线程池中有空闲的线程则复用线程,故输出结果:

pool-1-thread-2正在运行
pool-1-thread-6正在运行
pool-1-thread-4正在运行
pool-1-thread-3正在运行
pool-1-thread-1正在运行
pool-1-thread-5正在运行
pool-1-thread-5正在运行
pool-1-thread-6正在运行

可以明显看出线程pool-1-thread-5pool-1-thread-6被复用。

newSingleThreadExecutor:创建一个单线程的线程池,其保证任务按指定顺序执行。

public class ExecutorDemo {
	
	public static void main(String[] args) {
		ExecutorService executorService=Executors.newSingleThreadExecutor();
		for(int i=0;i<8;i++){
			executorService.submit(new ExecRunnalbe());
		}
		executorService.shutdown();  //关闭线程池
	}
	
}
输出结果:
pool-1-thread-1正在运行
pool-1-thread-1正在运行
pool-1-thread-1正在运行
pool-1-thread-1正在运行
pool-1-thread-1正在运行
pool-1-thread-1正在运行
pool-1-thread-1正在运行
pool-1-thread-1正在运行
可以看出线程池中始终只有唯一一个线程执行。

ExecRunnable类:

class ExecRunnalbe implements Runnable{
	
	@Override
	public void run(){
		System.out.println(Thread.currentThread().getName()+"正在运行");
	}
	
}

<think>好的,我需要回答用户关于如何在Java使用线程池的问题,包括示例代码和最佳实践。首先,我应该回顾用户提供的引用内容,看看有没有相关的信息可以利用。 根据引用[1],提到了Java线程池使用实例、最佳实践以及代码示例。引用[2]讨论了为什么使用线程池、如何使用以及遇到的坑。引用[3]则集中在最佳实践和异常处理方面。这些引用应该能提供足够的信息来构建回答。 首先,我需要解释线程池的基本概念和为什么要使用线程池,比如资源复用、控制并发数、管理线程生命周期等。然后,介绍Java中创建线程池的方法,特别是通过Executors工厂类提供的几种常见线程池类型,比如FixedThreadPool、CachedThreadPool、SingleThreadExecutor等,但也要提到推荐直接使用ThreadPoolExecutor来创建,以便更灵活地控制参数,避免资源耗尽的风险,这点在引用[3]中有提到。 接下来,给出一个示例代码。根据引用[1]中的示例,可能需要展示如何通过ThreadPoolExecutor来创建线程池,设置核心线程数、最大线程数、队列容量等参数,并演示如何提交任务。同时,需要强调在阿里巴巴开发规范中推荐手动创建线程池,而不是使用Executors,这点可能来自引用[3]中的最佳实践。 然后,讨论最佳实践,比如合理配置线程数、使用有界队列、处理未捕获异常、正确关闭线程池等。这些内容在引用[3]中有涉及,比如异常处理和分析的重要性,以及关闭线程池的方法。 此外,要注意线程池的任务拒绝策略,如AbortPolicy、CallerRunsPolicy等,并说明在不同场景下如何选择合适的策略。这部分可能需要结合引用[2]中提到的使用过程中遇到的坑。 最后,生成相关问题,可能包括线程池的参数配置、不同线程池类型的区别、如何监控线程池状态、常见的线程池问题排查等,确保问题与用户查询内容相关,并帮助深化理解。 需要确保回答结构清晰,分步骤说明,代码正确,并且引用正确的位置。例如,在提到线程池参数配置时,引用[3]中的最佳实践;在提到任务拒绝策略时,引用[2]中的实际应用经验。 现在需要将以上思考整理成中文回答,确保符合用户要求的格式,使用正确的LaTeX语法,并在适当的位置添加引用标识。</think>### 如何在Java使用线程池? #### 1. **线程池的作用与优势** - **资源复用**:避免频繁创建/销毁线程,提升性能[^2]。 - **控制并发**:通过核心线程数、最大线程数限制资源占用[^3]。 - **任务管理**:使用队列缓冲任务,支持拒绝策略处理过载[^1]。 #### 2. **创建线程池** Java推荐通过`ThreadPoolExecutor`手动配置参数(而非`Executors`工厂类,避免潜在风险)[^3]: ```java int corePoolSize = 5; // 核心线程数 int maxPoolSize = 10; // 最大线程数 long keepAliveTime = 60L; // 空闲线程存活时间(秒) BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100); // 有界队列 ThreadPoolExecutor executor = new ThreadPoolExecutor( corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.AbortPolicy() // 默认拒绝策略:直接抛出异常 ); ``` #### 3. **提交任务** - 使用`execute()`提交`Runnable`任务: ```java executor.execute(() -> { System.out.println("Task executed by " + Thread.currentThread().getName()); }); ``` - 使用`submit()`提交`Callable`任务(可获取返回值): ```java Future<String> future = executor.submit(() -> { return "Result from thread " + Thread.currentThread().getName(); }); System.out.println(future.get()); // 阻塞获取结果 ``` #### 4. **最佳实践** - **参数配置**: - CPU密集型任务:核心线程数 ≈ CPU核心数[^3]。 - I/O密集型任务:核心线程数可适当增加(如2倍CPU核心数)。 - 使用有界队列避免内存溢出[^3]。 - **异常处理**: ```java executor.setRejectedExecutionHandler((r, executor) -> { // 自定义拒绝策略,如记录日志或降级处理 }); // 任务内捕获异常 executor.execute(() -> { try { // 业务逻辑 } catch (Exception e) { // 记录异常 } }); ``` - **关闭线程池**: ```java executor.shutdown(); // 等待已提交任务完成 // executor.shutdownNow(); // 立即终止所有任务(慎用) ``` #### 5. **常用线程池类型** - **FixedThreadPool**:固定线程数,适用于负载较重的场景。 ```java ExecutorService fixedPool = Executors.newFixedThreadPool(5); ``` - **CachedThreadPool**:弹性线程数,适合短时异步任务[^1]。 - **ScheduledThreadPool**:支持定时/周期性任务。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值