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类。我们在下一篇文章中再详细介绍这个类的用法。


发布了40 篇原创文章 · 获赞 32 · 访问量 11万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章