Java并发编程入门(十六)正确理解两类线程池

Java极客  |  做者  /  铿然一叶
这是Java极客的第 44 篇原创文章

1、例子一

网站的请求要记录调用耗时日志,此日志并非很是重要,不但愿影响核心业务流程,要在主流程以外独立处理,所以能够在请求调用完成后由独立线程来记录,处理方式有以下几种:java

1.每一个请求先将数据放入队列中,由已运行的线程从队列中获取后入库。编程

2.每一个请求启动一个新线程,将数据传给新线程处理入库。缓存

因为线程的建立和切换比较耗资源,所以第2种方式下每一个请求都要启动一个新的线程来处理无疑不可行。安全

在这个例子中,使用方式1处理就能够了,方式一中事先运行的几个线程也能够理解为是线程池,这些线程作的事情都是相同的。bash

2、例子二

商场开了一个DIY陶器店,每一个顾客能够本身制做陶器,根据测算,店里置办了5套制陶工具,十一假期人满为患,制陶工具不够用了,店主就到陶具店临时租了两套,假期结束后,客人少了又还回去,能够看到临时租陶具很耗资源。这个例子能够对应到代码中的线程池,以下:并发

1.Thread建立和销毁都很耗资源(租陶具和还陶具),所以不能频繁建立和销毁,而是事先准备好几个,这几个就是线程池的初始线程。工具

2.Thread(陶具)只是一个工具,至于Runnable(顾客)要作什么它不知道。post

3.Thread(陶具)能够复用,每一个Runnable(顾客)具体要作啥,那是Runnable(顾客)的事情。每一个Runnable(顾客)能够作相同的事情(陶器),也能够作不一样的事情(陶器),没有限制。测试

4.若是Thread(陶具)不够,Runnable(顾客)要排队等待。优化

Java中对应的类结构以下:

1.Executors能够理解为陶器店,经过newFixedThreadPool(nThreads:int)方法置办陶具。
2.ExecutorService刚才例子里没有,这里理解为店小二,负责安排客人。
3.Runnable就是客人了。

下面看代码理解一下。

3、Show me code

I、Customer.java

public class Customer implements Runnable {

    //顾客名
    private final String customerName;

    //制做陶器名
    private final String porcelainName;

    public Customer(String customerName, String porcelainName) {
        this.customerName = customerName;
        this.porcelainName = porcelainName;
    }

    public void run() {
        makePorcelain();
    }

    //制做陶器
    private void makePorcelain() {
        System.out.println(Thread.currentThread().getName() + ": " + customerName + " made a " + porcelainName + ".");
    }
}
复制代码

II、ThreadPoolTest.java

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/** * @ClassName ThreadPoolTest * @Description 线程池测试 * @Author 铿然一叶 * @Date 2019/10/9 23:29 * @Version 1.0 * javashizhan.com **/
public class ThreadPoolTest {

    public static void main(String[] args) {

        //店铺开张置办了3套陶具,交给店小二打理
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        //店小二安排顾客制做陶具,陶具不够排队等待
        executorService.execute(new Customer("甲", "vase"));
        executorService.execute(new Customer("乙", "bowl"));
        executorService.execute(new Customer("丙", "plate"));
        executorService.execute(new Customer("丁", "teapot"));

        //店小二关门打烊
        executorService.shutdown();
    }
}
复制代码

输出日志:

pool-1-thread-2: 乙 made a bowl.
pool-1-thread-3: 丙 made a plate.
pool-1-thread-1: 甲 made a vase.
pool-1-thread-2: 丁 made a teapot.

Process finished with exit code 0
复制代码

1.经过线程名pool-1-thread-X能够看到线程池(店里)只有3个Thread(陶具)。

2.因为陶具不够,顾客丁要等待顾客乙制做完后才能使用陶具。

4、总结

1.两个例子均可以说用到了线程池,在谈论的时候要先理解对方说的是哪种线程池。

2.例子一线程池里的线程能够理解为全包,你要作啥,线程全帮你作。

3.例子二线程池里的线程就是半包,线程池提供基础设施(工具),要作啥你本身作。

4.若是全部线程要作的事情是固定的,那么采用例子一中的方式基本都能搞定,当每一个线程要作的事情不肯定时,要使用Executors。固然,也没有限制即便要作的事情是固定的就不能用Executors。

5.有大厂不建议使用Executors来建立线程,缘由是可能因为要执行的Runnable(顾客)太多而致使内存溢出,但在例子一中使用数据队列方式处理时,若是数据太多也同样可能溢出,因此,实际使用还要看业务场景,不要因噎废食。

本篇的主要目的是理解线程池概念,这个理解了,Executors的其余用法也就不难理解了。

end.


相关阅读:
Java并发编程(一)知识地图
Java并发编程(二)原子性
Java并发编程(三)可见性
Java并发编程(四)有序性
Java并发编程(五)建立线程方式概览
Java并发编程入门(六)synchronized用法
Java并发编程入门(七)轻松理解wait和notify以及使用场景
Java并发编程入门(八)线程生命周期
Java并发编程入门(九)死锁和死锁定位
Java并发编程入门(十)锁优化
Java并发编程入门(十一)限流场景和Spring限流器实现
Java并发编程入门(十二)生产者和消费者模式-代码模板
Java并发编程入门(十三)读写锁和缓存模板
Java并发编程入门(十四)CountDownLatch应用场景
Java并发编程入门(十五)CyclicBarrier应用场景
Java并发编程入门(十七)一图掌握线程经常使用类和接口
Java并发编程入门(十八)再论线程安全


Java极客站点: javageektour.com/

<---码字不易,左边点赞!

相关文章
相关标签/搜索