Java中线程池实现的两种方式

01.png

01 线程池的应用场景

(1)应用

比如现在收集上的修图软件。一张 1920 x1080 的图片有 200多万个像素点,对整个图片的每个像素点处理一遍也是需要不少的计算量。

(2)服务器端

服务器端处理大数据、大量请求时如果只是单个线程来进行,也是无法满足需求的。

此外,不管是处理应用还是服务器,即使使用了多线程,如果频繁进行创建和销毁线程,最终创建和销毁的时间有可能大于真正执行的时间。将对象复用是一个不错的选择,因此可以选择线程池。

线程池涉及到的常用的 Java 类包括 ThreadPoolExecutor、Executors 等。

02 ThreadPoolExecutor

(1)ThreadPoolExecutor 创建对象较为复杂,如下图是在 JDK 11 中提供的几个构造函数。

02.png

这些参数主要涉及到线程池一些关键参数的设置。以参数最多的这个构造函数为例:

public ThreadPoolExecutor(int corePoolSize, 
                                                    int maximumPoolSize, 
                                                    long keepAliveTime, 
                                                    TimeUnit unit, 
                                                    BlockingQueue<Runnable> workQueue, 
                                                    ThreadFactory threadFactory, 
                                                    RejectedExecutionHandler handler) {
    ...
}

corePoolSize 空闲时线程池中保存的线程数。

maximumPoolSize 线程池中允许的最大线程数。

keepAliveTime 当有空闲线程时,且现有的线程数大于 corePoolSize 在超过该指定的时间则删除该线程,在指定的时间内并不删除线程。

unit 指设置的 keepAliveTime 的时间单位。

workQueue 执行前用来保存任务队列。

threadFactory 指定创建自定义线程的工厂类。

handler RejectedExecutionHandler 的作用是当线程池关闭之后如有任务加入的时候,可以实现一些处理,例如将被拒绝的线程信息记录下来。

(2)execute() 和 submit() 方法

两者都是能像线程池提交任务的,但是细节会有不同。

execute() 可以添加一个任务,但是类型只能是Runnable 类型,且该任务不能返回结果。当遇到自线程异常时,直接打印异常信息,主线程无法捕获异常信息。

submit() 可以添加一个任务,但是返回一个 Future 对象,并能够通过 Future 获得返回结果。当遇到异常时,能够在主线程中捕获异常信息。

submit() 方法的参数涉及到两类 callable 接口和 Runnable ,它们的主要区别是:

(a)Callable 接口的 call() 方法可有返回值,而 Runnable 接口的 run() 方法没有返回值。

(b)Callable 接口的 call() 方法可以生命抛出异常,而 Runnable 接口的 run() 方法不可以声明抛出异常。

(3)其他常用方法

shutdown() 主线程立即结束,且不会阻塞,线程池中为执行完的线程会继续执行,线程池中不在添加新的 Task,线程池会继续运行知道线程都执行完成。

当线程会立即编程 SHUTDOWN 状态,此时不再想线程池中添加 Task,否则抛出 RejectedExecutionException 异常。

shutdownNow()中断所有的任务,并抛出 InterruptException 异常。执行后立即进入 STOP 状态,并试图停止所有正在执行的线程,不再处理还在线程池中等待的任务。

该方法执行后会返回未执行的任务:List<Runnable>

isShutdown() 判断线程池是否已经关闭。

isTerminating() 是否正在终止的过程中时,返回 true。

isTerminated() 判断所有任务都已经完成。

awaitTermination() 查看在指定的时间之间,线程池是否已经终止工作。

03 Executors

Executors 是线程池的创建类,能够方便通过已经提供的方法的创建线程池。

(1)Executors 提供的常用方法:

newCachedThreadPool() 创建无边界的线程池,可以进行线程自动回收,所谓“最大线程池”就是池中存放线程数位理论上的最大值, Integer.MAX_VALUE 最大值。

newCachedThreadPool(ThreadFactory) 自定义线程池中的线程

newFixedThreadPool(int) 创建有边界的线程池

newFixedThreadPool(int, ThreadFactory) 创建自定义线程的有边界线程池。

newSingleThreadExector() 创建单一线程池,可实现以队列的方式执行任务。

newSingleThreadExecutor() 用工厂方式创建单一线程池。

04 案例

(1)使用 ThreadPoolExecutor 创建线程池

package com.page.concurrent.pool;

import java.util.concurrent.*;

public class Game {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                1, 2, 1000 * 5, TimeUnit.SECONDS, new LinkedBlockingDeque<>());

        Future<String> future = threadPoolExecutor.submit(() -> {
            System.out.println("Should work 4 seconds.");
            Thread.sleep(1000 * 4);
            System.out.println("Work done.");
            return "OK";
        });

        String result = future.get();
        System.out.println("Thread response result is " + result);
        threadPoolExecutor.shutdownNow();
        System.out.println("Thread pool status: isShutdown=" + threadPoolExecutor.isShutdown());
        Thread.sleep(1000 * 5);
        System.out.println("Thread pool status: isTerminated" + threadPoolExecutor.isTerminated());
    }
}

(2)使用 Executors 创建线程池

package com.page.concurrent.pool;

import java.util.concurrent.*;

public class Game {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();

        Future<String> future = executorService.submit(() -> {
            System.out.println("Should work 4 seconds.");
            Thread.sleep(1000 * 4);
            System.out.println("Work done.");
            return "OK";
        });

        String result = future.get();
        System.out.println("Thread response result is " + result);
        executorService.shutdownNow();
        System.out.println("Thread pool status: isShutdown=" + executorService.isShutdown());
        Thread.sleep(1000 * 5);
        System.out.println("Thread pool status: isTerminated" + executorService.isTerminated());
    }
}

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

推荐阅读更多精彩内容