使用线程池和直接new 一个Thread运行对比

本文通过实验对比了使用线程池与频繁创建线程的效率差异,结果显示线程池能显著提高多线程任务的执行效率。

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

大家new Thread的方式会创建一个线程,在我们有大量的创建线程的时候这样的方法还会可靠吗?每一次new Thread都会重新创建一个线程,而线程的创建和销毁都需要耗时的。在jdk1.5的concurrent包中有一个Executors,他能使我们创建的线程得到复用,不会频繁的创建和销毁线程。

在网上已经有很多博文介绍了Executors了,我们今天主要是我们使用了Executors和每次new一个Thread的对比。

测试代码:

package com.ys.concurrent.test001;

import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test001 {
	private ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<String>();
//	private ArrayList<String> queue = new ArrayList<String>();
//	private CyclicBarrier barrier = new CyclicBarrier(10000000);
	private CountDownLatch latch = new CountDownLatch(100000);
	ExecutorService es = Executors.newFixedThreadPool(4);
	public static void main(String[] args) {
		Test001 test001 = new Test001();
		long timeStart = System.currentTimeMillis();
		test001.start();
		System.out.println(System.currentTimeMillis()-timeStart);
	}
	
	public void start(){
		for (int i = 0; i < 100000; i++) {
			Runnable001 runnable001 = this.new Runnable001(i);
			es.submit(runnable001);
//			new Thread(runnable001).start();
		}
		es.shutdown();
		try {
			//等待latch计数为0
			latch.await();
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println(queue.size());
	}
	
	private class Runnable001 implements Runnable{
		private int value;
		public Runnable001(int value) {
			this.value = value;
		}
		@Override
		public void run() {
			try {
//				barrier.await();
			} catch (Exception e) {
				e.printStackTrace();
			}
			queue.offer(value+"");
			latch.countDown();//latch计数减一
		}
		
	}
}

首先是使用Executors的情况:运行结果如下:


注释:es.submit(runnable001);放开:new Thread(runnable001).start();得到结果:


可见差距之大,(也不排除我使用的参数值不合理的情况);但是我们如果有大量的需要使用线程的话不妨考虑一下线程池。

<think>好的,我现在需要回答用户关于为什么推荐直接使用ThreadPoolExecutor作为Java线程池实现的问题。首先,我得回顾一下ThreadPoolExecutor的基本知识。ThreadPoolExecutor是Java中用于创建线程池的核心类,它提供了高度可定制的参数,比如核心线程数、最大线程数、存活时间等。用户可能想知道为什么直接使用它而不是其他更高级的工厂方法,比如Executors类提供的newFixedThreadPool或newCachedThreadPool。 首先,我记得Executors工厂方法虽然方便,但可能会隐藏一些配置细节。比如newFixedThreadPool使用无界队列,可能导致内存问题。而直接使用ThreadPoolExecutor可以让开发者显式地设置这些参数,从而更好地控制资源使用。这一点应该作为第一个理由,强调显式控制的重要性。 其次,资源管理的优化。使用ThreadPoolExecutor可以避免Executors中某些预设配置的缺陷,比如newCachedThreadPool可能创建过多线程,导致系统负载过高。直接配置核心线程数、最大线程数队列类型,能有效防止资源耗尽。这里需要提到不同的队列策略,比如LinkedBlockingQueueSynchronousQueue的区别,以及如何根据需求选择合适的队列。 第三,拒绝策略的自定义。ThreadPoolExecutor允许开发者自定义当任务无法处理时的拒绝策略,比如抛出异常、丢弃任务或由调用线程执行任务。而Executors提供的默认策略可能不符合特定场景需求,比如AbortPolicy直接抛出异常,但在某些情况下可能需要更灵活的处理方式。这里需要举例说明不同的拒绝策略及其适用场景。 第四,线程工厂钩子方法的扩展性。通过自定义ThreadFactory,可以设置线程的名称、优先级等,便于监控调试。此外,ThreadPoolExecutor提供的beforeExecuteafterExecute方法允许在任务执行前后添加日志或统计信息,这对性能监控很有帮助。这部分需要说明扩展性带来的好处,比如更好的可维护性。 然后,需要结合用户提供的引用内容。引用[1]中提到ThreadPoolExecutor允许通过方法动态调整参数,比如setCorePoolSize,这提供了运行时调整的灵活性,而Executors创建的线程池可能不支持这些调整。因此,动态调整能力也是一个重要理由。 同时,用户提供的引用[2][3]涉及安全相关的异常,虽然与线程池直接相关,但可以间接说明显式控制配置有助于避免潜在的安全或资源问题,比如通过合理设置线程池参数防止资源耗尽导致系统不稳定。 最后,需要总结直接使用ThreadPoolExecutor的优势,并可能提到在需要高度定制优化的场景下,直接使用它比使用Executors更合适。同时,确保回答符合用户的要求,比如结构清晰、使用中文、正确的LaTeX格式,以及生成相关问题。 现在需要检查是否有遗漏的点,比如线程池的工作原理,如如何复用线程、keepAliveTime的作用等,但用户问题主要是关于推荐使用的原因,所以可能不需要深入细节。但可以简要提到这些机制如何通过ThreadPoolExecutor的参数进行控制,以支持理由。 另外,用户示例中引用了动态设置参数的方法,如setKeepAliveTime,这也是直接使用ThreadPoolExecutor的优势之一,因为Executors返回的线程池可能不允许这些设置,或者需要类型转换才能访问这些方法。例如,Executors.newFixedThreadPool返回的是ExecutorService接口,而实际实现是ThreadPoolExecutor,但需要强制转换后才能调用这些方法,这可能不太方便。因此,直接使用ThreadPoolExecutor实例可以更方便地调整参数。 总结起来,主要理由包括:显式控制参数、避免资源耗尽、自定义拒绝策略、线程工厂钩子方法、动态调整能力。这些点需要分条列出,并辅以例子或引用中的内容支持。例如,引用[1]中的方法如setCorePoolSize支持动态调整,这增强了灵活性,而Executors创建的线程池可能需要额外处理才能调整这些参数。 最后,生成的相关问题需要围绕线程池的配置、参数调优、拒绝策略比较、与Executors的对比等方向来设计,确保问题具有针对性扩展性。</think>### 为什么推荐直接使用ThreadPoolExecutor作为Java线程池实现? 在Java中,创建线程池时推荐直接使用$ThreadPoolExecutor$类而非$Executors$工厂方法,主要原因如下: --- #### 1. **显式控制线程池参数** $ThreadPoolExecutor$允许开发者**直接配置核心参数**,包括: - 核心线程数($corePoolSize$) - 最大线程数($maximumPoolSize$) - 线程存活时间($keepAliveTime$) - 任务队列类型(如$LinkedBlockingQueue$或$SynchronousQueue$) 例如,使用$Executors.newFixedThreadPool()$会默认使用无界队列$LinkedBlockingQueue$,可能导致任务堆积内存溢出(OOM)。而直接使用$ThreadPoolExecutor$可以显式选择有界队列,避免资源耗尽[^1]。 --- #### 2. **优化资源管理** 通过自定义队列线程数策略,开发者可以更精细地平衡**性能与资源消耗**: - 有界队列可限制任务数量,防止内存溢出。 - 合理设置$maximumPoolSize$避免过多线程导致系统过载。 - 通过$keepAliveTime$回收空闲线程,减少资源占用[^1]。 --- #### 3. **自定义拒绝策略** $ThreadPoolExecutor$支持自定义$RejectedExecutionHandler$,处理任务队列满时的场景。例如: - **AbortPolicy**:直接抛出异常(默认策略)。 - **CallerRunsPolicy**:由提交任务的线程执行任务。 - **自定义策略**:如记录日志或降级处理。 相比之下,$Executors$的默认策略可能不适用于复杂场景。 --- #### 4. **动态调整能力** 通过$ThreadPoolExecutor$提供的方法(如$setCorePoolSize()$、$setMaximumPoolSize()$),可在运行时**动态调整线程池参数**,适应流量波动。而$Executors$创建的线程池可能需要类型转换才能实现这一功能。 --- #### 5. **扩展性与监控支持** - **自定义线程工厂**($ThreadFactory$):可设置线程名称、优先级等,便于问题排查。 - **钩子方法**(如$beforeExecute()$、$afterExecute()$):支持添加日志、性能监控等逻辑。 --- #### 6. **规避Executors的潜在风险** $Executors$的部分方法存在设计缺陷: - $newCachedThreadPool()$可能因无限创建线程导致CPU负载过高。 - $newSingleThreadExecutor()$使用无界队列,同样有OOM风险。 --- ### 示例代码:直接创建ThreadPoolExecutor ```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 CustomThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy() ); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值