java线程池学习(三) —— ThreadPoolExecutor

上一篇文章中我们自己写了一个简单的线程池。

这一篇文章我们来了解一下java为我们提供的线程池实现—— ExecutorService接口

它位于jdk的java.util.concurrent包下。

JDK提供了这么两个类来实现这个接口:

  • ThreadPoolExecutor
  • ScheduledThreadPoolExecutor

我们这篇文章只介绍一下ThreadPoolExecutor类(ScheduledThreadPoolExecutor类类似,多加入了计划任务功能。)

我们首先看看怎么用ThreadPoolExecutor类初始化一个线程池:

//初始化一个线程池
//核心线程数
int  corePoolSize  = 5;
//最大线程数
int  maxPoolSize   = 10;
//空闲线程最大存活时间
long keepAliveTime = 5000;
//任务队列
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(5);
        
ExecutorService threadPoolExecutor =
        new ThreadPoolExecutor(
                corePoolSize,
                maxPoolSize,
                keepAliveTime,
                TimeUnit.MILLISECONDS,
                queue
        );

这个类完全实现了一个类似于我们上一篇文章中实现的线程池,它包含以下几个属性:

1. corePoolSize 

 核心线程数:即使没有任何任务过来,线程池里面也会有保持的最基本线程数。

 2. maximumPoolSize

 最大线程数(即使任务特别多,线程池里的线程数也不会超过它)

 3. keepAliveTime

 空闲线程最大存活时间

 4. blockingQueue

 任务队列,用来存放待处理的任务。我们在这个系列的第一篇文章中就介绍过了它。

 可以选择以下几个阻塞队列。

  1. ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
  2. LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。
  3. SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue。
  4. PriorityBlockingQueue:一个具有优先级得无限阻塞队列。  

那么整个类的结构就如下图所示:

要使用这个线程池的话,可以使用它提供给我们的如下方法:

  • execute(Runnable)
  • submit(Runnable)
  • submit(Callable)
  • invokeAny(...)
  • invokeAll(...)

execute()和submit()可以向这个线程池提交单个任务。他们的区别是:

使用execute提交任务,但是execute方法没有返回值,所以无法判断任务知否被线程池执行成功。

使用submit 方法来提交任务,它会返回一个future对象,那么我们可以通过这个future对象来判断任务是否执行成功

invokeAll可以直接把一个List类型的任务列表一次性的提交给线程池执行。

==================================================================================

那么接下来我们就用 ThreadPoolExecutor 来创建一个线程池,改写一下我们上一篇文章的例子:

public class ProblemCreater {

	public static void main(String[] args) throws Exception {
		//初始化线程池
		//核心线程数
		int  corePoolSize  = 5;
		//最大线程数
		int  maxPoolSize   = 10;
		long keepAliveTime = 5000;
                
		ExecutorService threadPoolExecutor =
		        new ThreadPoolExecutor(
		                corePoolSize,
		                maxPoolSize,
		                keepAliveTime,
		                TimeUnit.MILLISECONDS,
		                new ArrayBlockingQueue<Runnable>(5)
		        );
		
		//生成者不断产生任务
		for(int i=1;i<10;i++){
			//定义一个新的任务
			Runnable task = new Runnable(){
				public void run(){
					Random random = new Random();
					//随机一个数字模拟需要解决的时间
					int randomTime = Math.abs(random.nextInt())%20;
					try {
						Thread.sleep(randomTime*1000);
						System.out.println("任务完成,花费时间为:"+randomTime);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			};
			//将问题插入到线程池任务队列中
			threadPoolExecutor.execute(task);
			System.out.println("插入新的任务"+i);
		}
	}
}

我们再回头看看这个 ThreadPoolExecutor 的初始化,我们需要给它传递5个参数。核心线程数,最大线程数,线程存活时间,时间单位,还有阻塞队列的类型。

这个过程还是比较繁琐的。其实Java帮我们简化了这个过程,我们可以根据不同的情景,直接用一行代码创建一个合适的线程池。

实现这个功能的就是 java.util.concurrent.Executors类。我们在下一篇文章中再详细介绍这个类的用法。


Java线程池是一种用于管理和复用线程的机制,它可以帮助我们更好地管理线程,防止线程过多导致系统资源的浪费和性能问题。线程池通过一个池子来缓存和复用线程,让线程可以被重复利用,从而减少线程的创建和销毁的开销,提高系统的性能。 在Java中,线程池是通过java.util.concurrent包下面的Executor框架来实现的。Executor框架提供了一种将任务提交与执行分离开来的机制,它将任务的提交和执行分离开来,从而使得任务的执行更加高效和灵活。 Java线程池的主要特点包括: 1. 线程复用:线程池中的线程可以被重复利用,从而减少线程的创建和销毁的开销,提高系统的性能。 2. 控制线程数量:通过控制线程池中的线程数量,可以避免线程过多导致系统资源的浪费和性能问题。 3. 线程池大小自适应:线程池的大小可以根据需要自适应调整,以适应不同的任务负载。 4. 任务队列:线程池中通常会设置一个任务队列,用于存放等待执行的任务。 5. 线程池管理:线程池通常会提供一些管理方法,用于监控线程池的状态和执行情况。 Java线程池的使用步骤如下: 1. 创建一个线程池对象。 2. 向线程池中提交任务。 3. 线程池会自动分配线程来执行任务。 4. 等待任务执行完成。 5. 关闭线程池线程池的具体实现可以通过Java提供的ThreadPoolExecutor类来完成。ThreadPoolExecutor类提供了一些构造方法和方法,可以用来设置线程池的参数和管理线程池。同时,Java还提供了一些其他类型的线程池,例如FixedThreadPool、CachedThreadPool和ScheduledThreadPool等,可以根据需要选择不同类型的线程池来处理任务。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值