Java线程池全面解析

开篇:为什么要使用线程池?

在回答这个问题之前我们先想想为什么要使用多线程?
answer:由于计算机处理器的处理速度极大的超过了计算机存储和通信子系统的处理速度,在单线程情况下当进行磁盘IO操作或者进行网络请求时会导致处理器大部分时间处于空闲状态,这就造成了处理器资源的浪费。同时Android中规定主线程只能进行UI操作,对于耗时操作则需要放到子线程,否则可能会导致ANR。所以为了尽量的“压榨”处理器的效率,使得计算机同时能够处理多个任务,提高任务执行效率,Java引入了多线程。

那为什么要使用线程池呢?
answer:对于每个jvm都有限定的内存大小,若无限制的去创建新线程轻者导致虚拟机频繁的GC造成卡顿,重者直接oom导致虚拟机崩溃。并且计算机处理器的并发处理数量也是有限的,超过一定数量的线程并不会加快处理器处理的效率。所以为了维护Java虚拟机环境的绿色稳定,充分利用处理器的速度,统一管理线程,对线程按照一定规则进行调度处理,Java引入了线程池。

so:如何去创建线程池?

我们可以直接使用ThreadPoolExecutor对象来创建线程池对象,也可以通过Java提供的四种线程类来快速的创建线程池对象。

代码:通过ThreadPoolExecutor创建线程池

ExecutorService service = new ThreadPoolExecutor(3,3,
                10, TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>());

/**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters and default thread factory.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

corePoolSize:核心线程数,默认一直存在于线程池中,除非allowCoreThreadTimeOut设置为true,则核心线程也会在空闲keepAliveTime之后回收。
maximumPoolSize:最大线程数,包含核心线程和非核心线程,非核心线程在空闲keepAliveTime之后会回收。
keepAliveTime:空闲线程存活时间。
unit:时间单位(时分秒啥的)。
workQueue:工作队列,所有需要执行的Runnable对象都会被加入到此队列中。
threadFactory: 创建线程的工厂类,默认DefaultThreadFactory。
handler:RejectedExecutionHandler接口对象,默认AbortPolicy,当队列达到限额时会阻塞然后执行RejectedExecutionHandler中的rejectedExecution方法。

代码:通过Executors的静态方法创建线程池对象。
通过上述对ThreadPoolExecutor构造器的分析,我们就能很好的理解下面四种线程池的特点。
1、FixedThreadPool,只有核心线程的线程池,空闲状态下默认不回收。代码如下图。

ExecutorService service = Executors.newFixedThreadPool(3);

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

2、CachedThreadPool,只有非核心线程的线程池,线程空闲超过60秒将会被回收。代码如下图。

ExecutorService service = Executors.newCachedThreadPool();

public static ExecutorService newCachedThreadPool() {
      return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
 }

3、ScheduledThreadPool,既有核心线程(由corePoolSize指定)又有非核心线程的线程池,非核心线程数的最大值为Integer.MAX_VALUE。代码如下图。

ExecutorService service = Executors.newScheduledThreadPool(3);

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
 }

4、SingleThreadExecutor,只有一个核心线程的线程池。代码如下。

ExecutorService service = Executors.newSingleThreadExecutor();

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

最后:使用线程池对象去执行线程方法。

通过ExecutorService提供的execute、submit方法,将线程添加到线程池中进行执行处理。

代码:通过execute(Runnable runnable)往线程池添加Runnable对象

service.execute(new Runnable() {
      @Override
      public void run() {
             System.out.println("我在子线程执行,呵呵");
      }
);

代码:通过submit(Callable<T> callable)往线程池添加Callable对象

Future<Integer> task = service.submit(new Callable<Integer>() {
      @Override
      public Integer call() throws Exception {
          System.out.println("我也在子线程执行,呵呵");
          return 66;
      }
});
//将阻塞当前线程直到task获取到返回值66
task.get();

代码:通过submit(Runnable runnable)往线程池添加Runnable对象

Future task1 = service.submit(new Runnable() {
      @Override
      public void run() {
          System.out.println("我也在子线程执行,呵呵");
      }
});
        
//将阻塞当前线程直到task获取到返回值null
ask1.get();

代码:通过submit(Runnable runnable, T result)往线程池添加Runnable对象并指定返回值result

Future<String> task2 = service.submit(new Runnable() {
    @Override
    public void run() {
        System.out.println("我也在子线程执行,呵呵");
    }
  }, "hello");

 //将阻塞当前线程直到task获取到返回值hello
 task2.get();

这样我们就可以创建和使用线程池了,写得不好也点个赞吧,呵呵。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352