Java线程池详解 — 如何使用线程池,及其注意事项与调优!

服务端应用程序(如数据库和 Web 服务器)需要处理来自客户端的高并发、耗时较短的请求任务,所以频繁的创建处理这些请求的所需要的线程就是一个非常消耗资源的操作。

常规的方法是针对一个新的请求创建一个新线程,虽然这种方法似乎易于实现,但它有重大缺点。为每个请求创建新线程将花费更多的时间,在创建和销毁线程时花费更多的系统资源。因此同时创建太多线程的 JVM 可能会导致系统内存不足,这就需要限制要创建的线程数,也就是需要使用到线程池。

记得点赞收藏加关注哦 ,需要下载PDF版本和获取更多知识点、面试题的朋友可以加q群:580763979   备注:简书   免费领取~

一、什么是 Java 中的线程池?

线程池技术就是线程的重用技术,使用之前创建好的线程来执行当前任务,并提供了针对线程周期开销和资源冲突问题的解决方案。 由于请求到达时线程已经存在,因此消除了线程创建过程导致的延迟,使应用程序得到更快的响应。

Java提供了以Executor接口及其子接口ExecutorService和ThreadPoolExecutor为中心的执行器框架。通过使用Executor,完成线程任务只需实现 Runnable接口并将其交给执行器执行即可。

为您封装好线程池,将您的编程任务侧重于具体任务的实现,而不是线程的实现机制。

若要使用线程池,我们首先创建一个 ExecutorService对象,然后向其传递一组任务。ThreadPoolExcutor 类则可以设置线程池初始化和最大的线程容量。

上图表示线程池初始化具有3 个线程,任务队列中有5 个待运行的任务对象。

执行器线程池方法

在固定线程池的情况下,如果执行器当前运行的所有线程,则挂起的任务将放在队列中,并在线程变为空闲时执行。

二、线程池示例

在下面的内容中,我们将介绍线程池的executor执行器。

创建线程池处理任务要遵循的步骤

创建一个任务对象(实现Runnable接口),用于执行具体的任务逻辑

使用Executors创建线程池ExecutorService

将待执行的任务对象交给ExecutorService进行任务处理

停掉 Executor 线程池

//第一步: 创建一个任务对象(实现Runnable接口),用于执行具体的任务逻辑 (Step 1)

class Task implements Runnable  {

    private String name;

    public Task(String s) {

        name = s;

    }

    // 打印任务名称并Sleep 1秒

    // 整个处理流程执行5次

    public void run() {

        try{

            for (int i = 0; i<=5; i++) {

                if (i==0) {

                    Date d = new Date();

                    SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");

                    System.out.println("任务初始化" + name +" = " + ft.format(d));

                    //第一次执行的时候,打印每一个任务的名称及初始化的时间

                }

                else{

                    Date d = new Date();

                    SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");

                    System.out.println("任务正在执行" + name +" = " + ft.format(d));

                    // 打印每一个任务处理的执行时间

                }

                Thread.sleep(1000);

            }

            System.out.println("任务执行完成" + name);

        }  catch(InterruptedException e)  {

            e.printStackTrace();

        }

    }

}

测试用例

public class ThreadPoolTest {

    // 线程池里面最大线程数量

    static final int MAX_SIZE = 3;

    public static void main (String[] args) {

        // 创建5个任务

        Runnable r1 = new Task("task 1");

        Runnable r2 = new Task("task 2");

        Runnable r3 = new Task("task 3");

        Runnable r4 = new Task("task 4");

        Runnable r5 = new Task("task 5");

        // 第二步:创建一个固定线程数量的线程池,线程数为MAX_SIZE

        ExecutorService pool = Executors.newFixedThreadPool(MAX_SIZE);

        // 第三步:将待执行的任务对象交给ExecutorService进行任务处理

        pool.execute(r1);

        pool.execute(r2);

        pool.execute(r3);

        pool.execute(r4);

        pool.execute(r5);

        // 第四步:关闭线程池

        pool.shutdown();

    }

}

示例执行结果

任务初始化task 1 = 05:25:55

任务初始化task 2 = 05:25:55

任务初始化task 3 = 05:25:55

任务正在执行task 3 = 05:25:56

任务正在执行task 1 = 05:25:56

任务正在执行task 2 = 05:25:56

任务正在执行task 1 = 05:25:57

任务正在执行task 3 = 05:25:57

任务正在执行task 2 = 05:25:57

任务正在执行task 3 = 05:25:58

任务正在执行task 1 = 05:25:58

任务正在执行task 2 = 05:25:58

任务正在执行task 2 = 05:25:59

任务正在执行task 3 = 05:25:59

任务正在执行task 1 = 05:25:59

任务正在执行task 1 = 05:26:00

任务正在执行task 2 = 05:26:00

任务正在执行task 3 = 05:26:00

任务执行完成task 3

任务执行完成task 2

任务执行完成task 1

任务初始化task 5 = 05:26:01

任务初始化task 4 = 05:26:01

任务正在执行task 4 = 05:26:02

任务正在执行task 5 = 05:26:02

任务正在执行task 4 = 05:26:03

任务正在执行task 5 = 05:26:03

任务正在执行task 5 = 05:26:04

任务正在执行task 4 = 05:26:04

任务正在执行task 4 = 05:26:05

任务正在执行task 5 = 05:26:05

任务正在执行task 4 = 05:26:06

任务正在执行task 5 = 05:26:06

任务执行完成task 4

任务执行完成task 5


如程序执行结果中显示的一样,任务 4 或任务 5 仅在池中的线程变为空闲时才执行。在此之前,额外的任务将放在待执行的队列中。

线程池执行前三个任务,线程池内线程回收空出来之后再去处理执行任务 4 和 5

使用这种线程池方法的一个主要优点是,假如您希望一次处理10000个请求,但不希望创建10000个线程,从而避免造成系统资源的过量使用导致的宕机。您可以使用此方法创建一个包含500个线程的线程池,并且可以向该线程池提交500个请求。

ThreadPool此时将创建最多500个线程,一次处理500个请求。在任何一个线程的进程完成之后,ThreadPool将在内部将第501个请求分配给该线程,并将继续对所有剩余的请求执行相同的操作。在系统资源比较紧张的情况下,线程池是保证程序稳定运行的一个有效的解决方案。

三、使用线程池的注意事项与调优

1.死锁 : 虽然死锁可能发生在任何多线程程序中,但线程池引入了另一个死锁案例,其中所有执行线程都在等待队列中某个阻塞线程的执行结果,导致线程无法继续执行。

2.线程泄漏 : 如果线程池中线程在任务完成时未正确返回,将发生线程泄漏问题。例如,某个线程引发异常并且池类没有捕获此异常,则线程将异常退出,从而线程池的大小将减小一个。如果这种情况重复多次,则线程池最终将变为空,没有线程可用于执行其他任务。

3.线程频繁轮换: 如果线程池大小非常大,则线程之间进行上下文切换会浪费很多时间。所以在系统资源允许的情况下,也不是线程池越大越好。

线程池大小优化: 线程池的最佳大小取决于可用的处理器数量和待处理任务的性质。对于CPU密集型任务,假设系统有N个逻辑处理核心,N 或 N+1 的最大线程池数量大小将实现最大效率。对于 I/O密集型任务,需要考虑请求的等待时间(W)和服务处理时间(S)的比例,线程池最大大小为 N*(1+ W/S)会实现最高效率。

不要教条的使用上面的总结,需要根据自己的应用任务处理类型进行灵活的设置与调优,其中少不了测试实验。

总结

我这里也准备了一线大厂面试资料和超硬核PDF技术文档,以及我为大家精心准备的多套简历模板(不断更新中),希望大家都能找到心仪的工作!

有需要的朋友可以加q群:580763979   备注:简书   免费领取~

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

推荐阅读更多精彩内容