3 - 线程池 Java内置的线程池 - ExecutorService
创始人
2024-03-29 00:26:36
0

1、ExecutorService的介绍

ExecutorService 接口继承了Executor 接口,是Executor 的子接口。

Executors类 提供工厂方法用来创建不同类型的线程池。Executors是工具类,他提供对ThreadPoolExecutor的封装,会产生几种线程池供大家使用。

关于 Executor 、ExecutorService、Executors三者的区别

参考:Executor, ExecutorService 和 Executors 间的区别与联系 - 夏威夷8080 - 博客园

UML简要类图关系:

2、ExecutorService 的获取

注:返回值都是 ExecutorService

3种方法以及每种方法对应的一个重载的方法

如重载方法:newCachedThreadPool(ThreadFactory threadFactory):

ThreadFactory 也是一个接口,该接口允许我们自己去写实现类,在实现类的内部只需要创建一个线程对象即可——这样的话,相当于我们自己(程序员)可以控制线程池中每一个线程对象的创建。

 上面的 “无界队列方式” —— 是指任务累加或者说缓存的时候,是不限制数量的。

(1)newCachedThreadPool() 方法、

以及其重载的方法法 newCachedThreadPool(ThreadFactory threadFactory)

:创建一个默认的线程池对象,里面的线程可重用,且在第一次使用时才创建;

重载的:线程池中的所有线程都使用ThreadFactory来创建,这样的线程无需手动启动,自动执行。

代码示例:MyTest01

package com.zhoulz.demo02;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;/*** 练习 Executors 获取ExecutorService,然后调用方法,提交任务;* newCachedThreadPool()方法、*         以及其重载的方法newCachedThreadPool(ThreadFactory threadFactory)*/
public class MyTest01 {public static void main(String[] args) {//test1(); // 下面test1()中的内容被提取出来了—— 快捷键Shift+Alt+M:提取方法Extract Method// 提取的原因是因为方便测试。接下来要测试newCachedThreadPool()重载的方法test2();}//(1)练习newCachedThreadPool()方法private static void test1() {// 1、使用工厂类——获取线程池对象final ExecutorService es = Executors.newCachedThreadPool();// 然后就可以调用方法 :如提交任务、shutdown()方法关闭提交// 2、提交任务 —— 任务可以是Runnable类型的对象,也可以是Callable类型的对象// 为了代码的整洁性,下面单独写了一个任务类。(也可以写一个匿名内部类)// 可以通过循环的方式来提交任务for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable(i));}}//(2)练习newCachedThreadPool()重载的方法—— (在里面直接传参数 ThreadFactory threadFactory)private static void test2() {// 1、使用工厂类——获取线程池对象final ExecutorService es = Executors.newCachedThreadPool(new ThreadFactory() {int n = 1;// 匿名内部类的内部,允许我们直接创建一个线程对象。所以下面return的时候,可以直接new@Overridepublic Thread newThread(Runnable r) {//return null;// 直接 new,并把Runnable的对象作为一个实参return new Thread(r,"自定义的线程名称" + n++);// 这样,创建出来的线程 new Thread()就可以和这个任务Runnable r 绑定在一起了!!!// 然后就可以执行了。。。// 还可以指定线程名称、加编号 n ...见上}});// 2、提交任务(这里是通过匿名内部类的方式,见上面)for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable(i));}}
}/*** 任务类:包含一个任务编号,在任务中,打印出是哪一个线程正在执行任务*/
class MyRunnable implements Runnable{// 为了对任务做一个区分,可以加一个编号,不能通过run()方法传进来,所以要通过构造方法private int id;public MyRunnable(int id) {this.id = id;}@Overridepublic void run() {// 获取线程的名称,打印一句话(这就是要执行的任务)String name = Thread.currentThread().getName();System.out.println(name + "执行了任务。。。" + id);}
}

结果:

test01:创建了10个线程

test02:—— 可见10个线程都是自定义创建的。

  

(2)newFixedThreadPool(int nThreads) 方法、以及其重载的方法 newFixedThreadPool(int nThreads, ThreadFactory threadFactory)

: 创建一个可重用固定线程数的线程池 ;

 重载的:创建一个可重用固定线程数的线程池且线程池中的所有线程都使用ThreadFactory来创建。

代码示例:MyTest02(基本同MyTest01)

package com.zhoulz.demo02;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;/*** 练习 Executors 获取ExecutorService,然后调用方法,提交任务;*(2)newFixedThreadPool(int nThreads)方法、以及其重载的方法*         newFixedThreadPool(int nThreads, ThreadFactory threadFactory)*/
public class MyTest02 {public static void main(String[] args) {//test1();test2();}//(1)练习newFixedThreadPool(int nThreads)方法private static void test1() {// 1、使用工厂类——获取线程池对象final ExecutorService es = Executors.newFixedThreadPool(3);// 2、提交任务for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable2(i));}}//(2)练习newCachedThreadPool()重载的方法private static void test2() {// 1、使用工厂类——获取线程池对象final ExecutorService es = Executors.newFixedThreadPool(3,new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r,"自定义的线程名称" + n++ +"-zlz");}});// 2、提交任务for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable2(i));}}
}/*** 任务类:包含一个任务编号,在任务中,打印出是哪一个线程正在执行任务*/
class MyRunnable2 implements Runnable{private int id;public MyRunnable2(int id) {this.id = id;}@Overridepublic void run() {// 获取线程的名称,打印一句话String name = Thread.currentThread().getName();System.out.println(name + "执行了任务。。。" + id);}
}

结果:

test01:只创建了3个线程 

test02自定义的创建了3个线程 

这是匿名内部类中自定义的线程对象(任务?)加了“-zlz”作区分

结合如下输出结果,可见 执行的任务/线程是匿名内部类里的,而不是MyRunnable2里的。

 但是为什么没执行呢?(下面还是提交了任务 new MyRunnable2(i) 的啊)

(3)newSingleThreadExecutor()方法、

以及其重载的方法 newSingleThreadExecutor(ThreadFactory threadFactory)

:创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程;

重载的:创建一个使用单个 worker 线程的 Executor,且线程池中的所有线程都使用ThreadFactory来创建。

代码示例:MyTest03(基本同MyTest01、MyTest02)

package com.zhoulz.demo02;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;/*** 练习 Executors 获取ExecutorService,然后调用方法,提交任务;*(3)newSingleThreadExecutor()方法、以及其重载的方法*        newSingleThreadExecutor(ThreadFactory threadFactory)*/
public class MyTest03 {public static void main(String[] args) {//test1();test2();}//(1)练习newFixedThreadPool(int nThreads)方法private static void test1() {// 1、使用工厂类——获取线程池对象final ExecutorService es = Executors.newSingleThreadExecutor();// 2、提交任务for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable3(i));}}//(2)练习newCachedThreadPool()重载的方法private static void test2() {// 1、使用工厂类——获取线程池对象final ExecutorService es = Executors.newSingleThreadExecutor(new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r,"自定义的线程名称" + n++ +"-zlz");}});// 2、提交任务for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable3(i));}}
}/*** 任务类:包含一个任务编号,在任务中,打印出是哪一个线程正在执行任务*/
class MyRunnable3 implements Runnable{private int id;public MyRunnable3(int id) {this.id = id;}@Overridepublic void run() {// 获取线程的名称,打印一句话String name = Thread.currentThread().getName();System.out.println(name + "执行了任务。。。" + id);}
}

结果:

test01:只创建了1个线程

test02:自定义的只创建了1个线程

submit()方法  —— 见上面的例子。

 3、shutdown()方法

以 newSingleThreadExecutor()方法、以及其重载的法 newSingleThreadExecutor(ThreadFactory threadFactory) 为例,进行举例。

代码示例:

package com.zhoulz.demo02;import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;/*** 练习 Executors 获取ExecutorService,测试关闭线程池的方法;* (以newSingleThreadExecutor()-单线程的获取方法为例)* 关闭线程数池的两种形式:*          1)直接关闭;*          2)关闭之后不再接收新的任务,以前的任务还会继续执行;*/
public class MyTest04 {public static void main(String[] args) {//test1();test2();}//(1)练习newFixedThreadPool(int nThreads)方法private static void test1() {// 1、使用工厂类——获取线程池对象final ExecutorService es = Executors.newSingleThreadExecutor();// 2、提交任务for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable4(i));}// 3、关闭线程池,仅仅是不再接收新的任务,以前的任务还会继续执行es.shutdown(); // 结果:1个线程把10个任务执行完了// 验证关闭线程池之后,还能不能再继续提交新的任务?结果:是不可以的//es.submit(new MyRunnable4(888));// 还有一个关闭方法 shutdownNow()—— 见test02}//(2)练习newCachedThreadPool()重载的方法private static void test2() {// 1、使用工厂类——获取线程池对象final ExecutorService es = Executors.newSingleThreadExecutor(new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r,"自定义的线程名称" + n++ +"-zlz");}});// 2、提交任务for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable4(i));}// 3、立刻关闭线程池,如果线程池中还有缓存的任务没有执行,则取消执行,并返回这些任务// 返回的集合怎么保存?用List集合进行保存。shutdownNow()方法的返回值就是ListList list = es.shutdownNow();// 可以对List集合进行遍历。也可以直接输出,但是前提得在任务类/线程类MyRunnable4中进行toString()方法的重写// toString()方法重写了,但是结果还是java.util.concurrent.FutureTask@14ae5a5 ???System.out.println(list);// 此时,同样不能提交新的任务}
}/*** 任务类:包含一个任务编号,在任务中,打印出是哪一个线程正在执行任务*/
class MyRunnable4 implements Runnable{private int id;public MyRunnable4(int id) {this.id = id;}@Overridepublic String toString() {return "MyRunnable4{" +"id=" + id +'}';}@Overridepublic void run() {// 获取线程的名称,打印一句话String name = Thread.currentThread().getName();System.out.println(name + "执行了任务。。。" + id);}
}

4、ExecutorService 的获取 —— 3种方法的总结

1)newCachedThreadPool()性能优先模式

        线程的数量是不做限制的。每次有任务来的时候,都是以任务优先,即优先执行任务,可以理解为性能最大化。(前提是服务器的硬件能跟得上,服务器压力会比较大)

2)newFixedThreadPool()要求性能一般,服务器的配置不是太高。

规定线程的数量,可以让服务器的压力不这么大。

3)newSingleThreadExecutor()追求绝对的安全

:只考虑安全,不考虑性能。如银行转账等场景。

相关内容

热门资讯

保存时出现了1个错误,导致这篇... 当保存文章时出现错误时,可以通过以下步骤解决问题:查看错误信息:查看错误提示信息可以帮助我们了解具体...
汇川伺服电机位置控制模式参数配... 1. 基本控制参数设置 1)设置位置控制模式   2)绝对值位置线性模...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
表格中数据未显示 当表格中的数据未显示时,可能是由于以下几个原因导致的:HTML代码问题:检查表格的HTML代码是否正...
本地主机上的图像未显示 问题描述:在本地主机上显示图像时,图像未能正常显示。解决方法:以下是一些可能的解决方法,具体取决于问...
表格列调整大小出现问题 问题描述:表格列调整大小出现问题,无法正常调整列宽。解决方法:检查表格的布局方式是否正确。确保表格使...
不一致的条件格式 要解决不一致的条件格式问题,可以按照以下步骤进行:确定条件格式的规则:首先,需要明确条件格式的规则是...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...