首页 技术 正文
技术 2022年11月11日
0 收藏 518 点赞 3,193 浏览 5184 个字

Java并发编程(您不知道的线程池操作)

这几篇博客,一直在谈线程,设想一下这个场景,如果并发的线程很多,然而每个线程如果执行的时间很多的话,这样的话,就会大量的降低系统的效率。这时候就可以采用线程池的操作,来缓存我们并发操作的线程。

而对于java中的线程池,大家需要理解好ThreadPoolExecutor、AbstractExecutorService、ExecutorService和Executor这几个类之间的关系即可。

Java并发编程(您不知道的线程池操作)

通过看上面的这个关系图,可以知道,最核心的一个类是ThreadPoolExecutor,我们下面来具体的看一下这个类的操作。

ThreadPoolExecutor类提供了四个构造函数,如下所示

<span style="font-family:Comic Sans MS;font-size:18px;"><span style="font-family:Comic Sans MS;"><span style="font-family:Comic Sans MS;font-size:18px;">public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);}public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,threadFactory, defaultHandler);}public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), handler);}public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 || maximumPoolSize <= 0|| maximumPoolSize < corePoolSize || keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;}</span></span></span>

下面来分析下这四个构造函数中每个参数的具体意义,其实主要是一些初始化的操作

corePoolSize:表明这个线程池的大小

     maximumPoolSize:表明这个线程池中,最多可以容纳多少个线程执行

     TimeUnit:线程存活的时间单位,有秒、小时、分钟等

     keepAliveTime:用来表示一个线程多长时间不执行任务后,就被废弃掉

     workQueue:阻塞队列,表示如何线程多余线程池的话,用来存储等待执行的任务,具体值如下


1)ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;

       2)LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;

       3)synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

 threadFactory:一个线程工厂,用来创建线程

     handler:用来表示拒绝执行任务的策略。

下面通过举个例子来理解上述的表示参数。

假如有一个工厂,工厂里面有10个工人,每个工人同时只能做一件任务。 因此只要当10个工人中有工人是空闲的,来了任务就分配给空闲的工人做;当10
个工人都有任务在做时,如果还来了任务,就把任务进行排队等待;如果说新任务数目增长的速度远远大于工人做任务的速度,那么此时工厂主管可能会想补救措
施,比如重新招4个临时工人进来;然后就将任务也分配给这4个临时工人做;如果说着14个工人做任务的速度还是不够,此时工厂主管可能就要考虑不再接收新
的任务或者抛弃前面的一些任务了。当这14个工人当中有人空闲时,而新任务增长的速度又比较缓慢,工厂主管可能就考虑辞掉4个临时工了,只保持原来的10
个工人,毕竟请额外的工人是要花钱的。

这个例子中的corePoolSize就是10,而maximumPoolSize就是14(10+4)。

也就是说corePoolSize就是线程池大小,maximumPoolSize在我看来是线程池的一种补救措施,即任务量突然过大时的一种补救措施。

下面通过一个例子来了解上述的构造函数。

<span style="font-family:Comic Sans MS;font-size:18px;"><span style="font-family:Comic Sans MS;"><span style="font-family:Comic Sans MS;font-size:18px;"><span style="font-family:Comic Sans MS;font-size:18px;">package com.test;import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class TestThreadPool {      public static void main(String[] args) {      //创建一个线程池        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 6, 5,TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());          //通过循环来开启20个任务操作         for (int i = 1; i <= 20; i++) {                threadPool.execute(new ThreadPoolTask());          }    }}//创建 ThreadPoolTask类: class ThreadPoolTask implements Runnable {    public void run() {        try {            System.out.println("开始执行任务:" + Thread.currentThread().getName());            Thread.sleep(100);        }        catch(Exception e){            e.printStackTrace();        }    }  }</span></span></span></span>

上面就是开启20个任务,来让线程池处理,如果线程池满了的话,就会放入到阻塞对列中进行等待操作

不过在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池:

Executors.newCachedThreadPool();        //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE

    Executors.newSingleThreadExecutor();   //创建容量为1的缓冲池
    Executors.newFixedThreadPool(int);    //创建固定容量大小的缓冲池


     下面是这三个静态方法的具体实现;

<span style="font-family:Comic Sans MS;font-size:18px;"><span style="font-family:Comic Sans MS;"><span style="font-family:Comic Sans MS;font-size:18px;">public static ExecutorService newFixedThreadPool(int nThreads) {    return new ThreadPoolExecutor(nThreads, nThreads,                                  0L, TimeUnit.MILLISECONDS,                                  new LinkedBlockingQueue<Runnable>());}public static ExecutorService newSingleThreadExecutor() {    return new FinalizableDelegatedExecutorService        (new ThreadPoolExecutor(1, 1,                                0L, TimeUnit.MILLISECONDS,                                new LinkedBlockingQueue<Runnable>()));}public static ExecutorService newCachedThreadPool() {    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                  60L, TimeUnit.SECONDS,                                  new SynchronousQueue<Runnable>());}</span></span></span>

     从它们的具体实现来看,它们实际上也是调用了ThreadPoolExecutor,只不过参数都已配置好了。

 newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;

            newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue;

  
       
 newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为
Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。

实际中,如果Executors提供的三个静态方法能满足要求,就尽量使用它提供的三个方法,因为自己去手动配置ThreadPoolExecutor的参数有点麻烦,要根据实际任务的类型和数量来进行配置。

另外,如果ThreadPoolExecutor达不到要求,可以自己继承ThreadPoolExecutor类进行重写。

     参考资料:       http://www.cnblogs.com/dolphin0520/p/3932921.html      http://blog.csdn.net/hejingyuan6/article/details/47058189

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,030
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,520
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,368
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,148
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,781
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,859