java线程池概述

来源:互联网 发布:万网域名续费多少钱 编辑:程序博客网 时间:2024/06/13 09:21

         池的概念不限于java语言,但是在java编程中我们经常会遇到各种池,数据库连接池(mysql,oracle,redis,mongodb,memcache),几乎没有web项目不使用到连接池的,再一个就是今天要说的线程池,在多线程编程中,线程需要不断的创建和销毁,这笔开销是很大的,如果控制不好,容易导致系统瘫痪。线程池技术,解决了多线程编程线程的创建,销毁的开销问题。

         以上其实说明了线程池的使用场景:第一、单个任务执行时间短,很快完成,第二、任务量大。

         线程池的思路:构建一些可用线程供使用,无需反复创建,使用完了归还,别的任务接着使用,在任务多的情况下他使用一个队列来做缓冲区。其实这个队列中保存的就是等待执行的任务线程,因此我们很容易理解BlockingQueue<Runnable>的含义,线程池示例如下所示:

package com.xxx.threadpool;import java.util.Scanner;import java.util.concurrent.Callable;import java.util.concurrent.CompletionService;import java.util.concurrent.ExecutorCompletionService;import java.util.concurrent.ExecutorService;import java.util.concurrent.Future;import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class ThreadPoolExecutorMain {private ThreadPoolExecutorMain(){}private static CompletionService<Object> service;private static LinkedBlockingQueue<Runnable> queue;static{queue = new LinkedBlockingQueue<Runnable>();ExecutorService executorService = new ThreadPoolExecutor(1,3,60L,TimeUnit.SECONDS,queue);service = new ExecutorCompletionService<Object>(executorService);}public static Future<Object> execute(String taskName){return service.submit(new Callable<Object>() {@Overridepublic Object call() throws Exception {System.out.println("run for task : "+taskName);return "task-"+taskName;}});}@SuppressWarnings("resource")public static void main(String[] args) {String input = null;while(true){input = new Scanner(System.in).nextLine();if("exit".equals(input))break;execute(input);}}}

执行结果:

这个示例无法看出线程池的作用,几乎和普通的Thread或者实现了Runnable接口的线程没什么区别,只是感受一下线程池的创建和执行任务的过程。

下面介绍线程池的类型,java线程池api为我们提供了使用Executors来创建线程池,通过它可以创建以下类型的线程池。

线程池的类型:

1、fixedThreadPool,线程池中可用线程数目固定,超过数目的任务等待有可用线程时执行,在空闲时不会释放资源。

2、cachedThreadPool,线程数无限制,可以创建从0到Integer.MAX_VALUE的线程,在空闲时可以释放资源,但是如果后续又有任务来,又需要重新创建线程。

3、singleThreadPool,保证线程池中始终只有一个可用线程,如果线程异常结束,则会有另一个线程被创建,可以保证任务的顺序执行,在任意时间不会有多余的活动线程。

4、scheduledThreadPool,可以指定线程数量,他的应用场景是执行定时任务或者需要重复执行的任务。

      通过以下部分源码可以看到:以上线程池的实现其实都是通过ThreadPoolExecutor来实现的,scheduledThreadPool更是实现了ScheduledExecutorService接口。  

public static ExecutorService newFixedThreadPool(int nThreads) {        return new ThreadPoolExecutor(nThreads, nThreads,                                      0L, TimeUnit.MILLISECONDS,                                      new LinkedBlockingQueue<Runnable>());    }
public static ExecutorService newCachedThreadPool() {        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                      60L, TimeUnit.SECONDS,                                      new SynchronousQueue<Runnable>());    }
public static ExecutorService newSingleThreadExecutor() {        return new FinalizableDelegatedExecutorService            (new ThreadPoolExecutor(1, 1,                                    0L, TimeUnit.MILLISECONDS,                                    new LinkedBlockingQueue<Runnable>()));    }
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {        return new ScheduledThreadPoolExecutor(corePoolSize);    }
public class ScheduledThreadPoolExecutor        extends ThreadPoolExecutor        implements ScheduledExecutorService

      通过一个示例来说明线程池之间的区别:创建4个任务,分别执行4次打印信息,每次打印之前sleep 100ms,而我们创建的线程池个数可以设定为3(cachedThreadPool和singleThreadPool无须设置),程序代码如下:

package com.xxx.threadpool;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;public class ThreadPoolMain {private static void run(ExecutorService threadPool){for(int i=1;i<5;i++){final int taskId = i;threadPool.execute(new Runnable() {@Overridepublic void run() {for (int n = 1; n < 5; n++) {try {TimeUnit.MILLISECONDS.sleep(100);} catch (Exception e) {e.printStackTrace();}System.out.println("第"+taskId+"次任务,第"+n+"次执行");}}});}threadPool.shutdown();}public static void main(String[] args) {ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);ExecutorService cachedThreadPool = Executors.newCachedThreadPool();ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);run(fixedThreadPool);//先执行前面的三个任务,最后执行第四个任务run(cachedThreadPool);//四个任务几乎同时执行run(singleThreadPool);//四个任务按照顺序执行,线程池中只有一个线程可用,只能依次执行每一个任务run(scheduledThreadPool);//此种场景下和第一种线程池执行结果类似,先执行前面三个任务,最后执行第四个任务}}

每次只运行一个线程池查看打印信息,分别如下图所示:

      在实际编程中,我们如何选择哪种线程池,其实不用过多的考虑,一般选择newFixedThreadPool(n),至于线程池中线程的个数,根据系统情况而定,一般为了充分利用系统cpu,编码中采用Runtime.getRuntime().availableProcessors()设置线程池的个数。

原创粉丝点击