文章目录

  • 1 场景
  • 2 直接创建线程
    • 2.1 Thread创建线程
    • 2.2 Runnable创建线程
    • 2.3 Callable创建线程
  • 3 自定义线程池
    • 3.1 关于线程池
    • 3.1 工具类创建
    • 3.2 自定义创建
      • 3.2.1 创建说明
      • 3.2.2 创建线程池
      • 3.2.3 使用线程池
      • 3.2.6 监视线程池
      • 3.2.5 关闭线程池
  • 4 spring线程池
    • 4.1 maven依赖
    • 4.2 定义线程池
    • 4.3 调用线程池
    • 4.4 注解调用
      • 4.4.1 开启注解
      • 4.4.2 使用异步方法
      • 4.4.3 多个线程池的情况

1 场景

java中默认情况下,程序都是同步执行的。即在一个线程中执行,程序未执行完,不返回请求。

如下场景,均需要创建执行程序外额外的线程:

  • 有些场景,需要立即返回请求结果,核心程序在后台异步执行,此次情况需要使用异步线程;
  • 处理数据较多时,需要启用多线程执行,提高执行速度

2 直接创建线程

java中可借助如下类,直接创建线程:ThreadRunnableCallable

此种方式,不建议使用。频繁地创建线程,会占用大量的资源和时间,会大大降低系统的性能

2.1 Thread创建线程

(1)类继承Thread类

public class ThreadClass extends Thread{private String param;public ThreadClass(String param){this.param=param;}@Overridepublic void run() {// 多线程方法}
}

启动线程,代码如下:

new ThreadClass("my param").start();

(2)使用匿名类

new Thread(){@Overridepublic void run() {// 多线程方法}
}.start();

2.2 Runnable创建线程

(1)类实现Runnable接口

public class RunnableClass implements Runnable {private String param;public RunnableClass(String param) {this.param = param;}@Overridepublic void run() {// 多线程方法}
}

启动线程,代码如下:

RunnableClass runnableClass = new RunnableClass("thread param");
new Thread(runnableClass).start();

(2)使用匿名类

new Thread(new Runnable(){@Overridepublic void run() {// 多线程方法}
}).start();

2.3 Callable创建线程

实现Callable接口创建的线程类,和Thread和Runnable的区别是:

  • 可以返回内容
  • 可以抛出异常

(1)类实现Callable接口

借助FutureTask执行线程,并获取返回结果

public class CallableClass implements Callable<String> {private String param;public CallableClass(String param) {this.param = param;}@Overridepublic String call() throws Exception {// 自定义抛出异常,可在主线程中调用get方法时捕获// throw new Exception("123");// 线程返回值return null;}
}

启动线程,并获取结果,代码如下:

FutureTask<String> futureTask = new FutureTask<>(new CallableClass("param value"));
Thread myThread = new Thread(futureTask);
myThread.start();
try {// 获取异步线程的返回值(调用此方法,会阻塞当前主线程)System.out.println(futureTask.get());
} catch (InterruptedException e) {// 线程被中断时抛出的异常e.printStackTrace();
} catch (ExecutionException e) {// Callable类中抛出的异常e.printStackTrace();
}

(2)使用匿名类

FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {@Overridepublic String call() throws Exception {// 线程返回值return null;}
});

3 自定义线程池

3.1 关于线程池

(1)为什么使用线程池?

程序中不允许直接创建线程使用,建议使用线程池。避险new的线程太多,程序中线程数量不可控,导致程序出问题。

(2)创建几个线程池?

一个应有中,可以创建多个线程池,根据实际业务情况服务器情况,创建对应的线程池,并调整成符合实际情况参数配置

建议相同场景的业务,使用同一个线程池,并进行文档记录

3.1 工具类创建

不建议此种方式创建线程池。

jdk中的工具类java.util.concurrent.Executors,是java提供的创建线程池的工具类。

此工具类强烈建议不要用,主要是因为此工具类创建的线程池,要么是线程池的排队队列无界队列(如Executors.newFixedThreadPool),要么是线程池的最大线程数为无界队列(如Executors.newCachedThreadPool)。

线程池中的最大线数排队队列数,如果为无界队列,线程池将会不可控,系统将会出现严重的问题。

说明:
此处的无界队列,指的是Integer.MAX_VALUE,并不是绝对的无限制。

不建议此种方式创建线程池,此处也不再讲解。

3.2 自定义创建

3.2.1 创建说明

创建线程池,有很多种方式,此处借助于ThreadPoolExecutor来创建线程池。

建议使用ThreadPoolExecutor最完整的构造函数创建线程池:

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

参数注释如下:

corePoolSize – the number of threads to keep in the pool, even if they are idle, unless allowCoreThreadTimeOut is set

maximumPoolSize – the maximum number of threads to allow in the pool

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.

unit – the time unit for the keepAliveTime argument

workQueue – the queue to use for holding tasks before they are executed. This queue will hold only the Runnable tasks submitted by the execute method.

threadFactory – the factory to use when the executor creates a new thread

handler – the handler to use when execution is blocked because the thread bounds and queue capacities are reached

参数补充说明:

(1)当前线程数小于corePoolSize,有新提交任务,将创建新的线程。即使当前线程池中有空闲线程

(2)当前线程数等于corePoolSize,有新提交任务,会将新任务放到队列workQueue中,只有在workQueue满了后且“maximumPoolSize >corePoolSize”,才会扩充当前线程数

(3)提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理

(4)当前线程数量大于corePoolSize,且线程空闲时间大于keepAliveTime后,空闲线程将被关闭。

(5)设置allowCoreThreadTimeOut为true时,线程数量不超过corePoolSize,空闲时间超过keepAliveTime后也会被关闭

3.2.2 创建线程池

(1)创建依赖类

/*** 自定义线程工厂(主要用于设置线程名称等属性)*/
public class MyThreadFactory implements ThreadFactory {private String threadName;private volatile AtomicInteger count = new AtomicInteger(0);public MyThreadFactory(String threadName) {this.threadName = threadName;}@Overridepublic Thread newThread(Runnable r) {Thread thread = new Thread(r);// 设置线程名String name = this.getClass().getSimpleName() + "_" + threadName + "_" + count.getAndAdd(1);thread.setName(name);return thread;}
}/*** 自定义拒绝策略(排队队列满了后,会执行此策略)* 默认拒绝策略AbortPolicy(拒绝任务,抛出异常)*/
public class MyRejectedExecutionHandler implements RejectedExecutionHandler {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {自定义拒绝策略 ---------------// (1)抛出异常,不执行任务/*throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + executor.toString());*/// (2)写入队列更改为阻塞方法try {// “非阻塞方法”offer改成“阻塞方法”putexecutor.getQueue().put(r);} catch (InterruptedException e) {e.printStackTrace();} 额外操作--------------------// (1)记录拒绝数量,进行日志记录及预警(根据此提示调整线程池配置或优化代码速度)// (2)加入补偿线程池去执行// (3)再次加入到线程池尝试执行//-----------------------------}
}

(2)创建线程池

// 线程排队队列(队列大小为100)
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100);
// 线程工厂
ThreadFactory threadFactory = new MyThreadFactory("testThread");
// 拒绝策略
RejectedExecutionHandler rejectedExecutionHandler = new MyRejectedExecutionHandler();
// 创建线程池
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 50, 15, TimeUnit.SECONDS, workQueue, threadFactory, rejectedExecutionHandler);
3.2.3 使用线程池

(1)调用Runnable

poolExecutor.execute(new Runnable() {@Overridepublic void run() {// 输出内容:MyThreadFactory_testThread_0System.out.println(Thread.currentThread().getName());}
});

(2)调用callable

 Future<String> future = poolExecutor.submit(new Callable<String>() {@Overridepublic String call() throws Exception {return Thread.currentThread().getName();}});
try {// 输出内容:MyThreadFactory_testThread_0System.out.println(future.get());
} catch (InterruptedException e) {e.printStackTrace();
} catch (ExecutionException e) {e.printStackTrace();
}
3.2.6 监视线程池

线程池的状态属性,可以作为检测线程池状态使用。

属性 说明
taskCount 返回已计划执行的任务的大致总数。线程的状态可能只在近似值期间动态地返回,因为任务的状态可能只在近似值期间发生变化。
completedTaskCount 返回已完成执行的任务的大致总数。由于任务和线程的状态在计算过程中可能会动态变化,因此返回的值只是一个近似值,但在连续调用期间不会减少。
largestPoolSize 返回池中同时存在的最大线程数。
poolSize 返回池中同时存在的最大线程数。
activeCount 返回正在积极执行任务的大约线程数。

可以启动一个单独的线程,将线程池对象作为属性传递进去,根据实际情况,定时检测线程池的相关属性,并进行记录。

此线程也可作为被检测线程池的一个线程任务使用。

3.2.5 关闭线程池

线程池不用时,可以进行关闭,实际项目中关闭线程池的情况很少,此节仅做一个记录。

可以调用shutdownshutdownNow关闭线程池。

(1)关闭机制

调用线程的interrupt方法来关闭线程。如线程无法响应,调用interrupt方法无法关闭线程,则线程池一直无法关闭

(2)shutdown和shutdownNow区别

shutdown:拒绝新任务,正在执行的任务,和队列中的任务都会执行

shutdownNow:拒绝新任务、停止正在执行的任务、停止队列中任务

调用任何一个方法,isShutdown返回结果都为true

所有任务都关闭后,线程池才真的被关闭,线程池真被关闭后isTerminaed 返回为true

4 spring线程池

如果java中集成了spring环境,建议使用spring创建线程池。

4.1 maven依赖

<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.2.RELEASE</version>
</dependency>

4.2 定义线程池

/*** 通用线程池* @return org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor*/
@Bean
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 核心线程数(默认为1)executor.setCorePoolSize(5);// 最大线程数(默认为:Integer.MAX_VALUE)executor.setMaxPoolSize(50);// 队列最大长度(默认为:Integer.MAX_VALUE)executor.setQueueCapacity(1000);// 线程池内线程允许的空闲时间/秒(默认60秒)executor.setKeepAliveSeconds(10);// 线程池拒绝策略(默认AbortPolicy:拒绝任务,抛出异常)executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());// 设置线程名前缀(用于定制线程名)executor.setThreadNamePrefix("common thread");// 设置线程分组名(用于定制分组信息)//executor.setThreadGroupName("common thread group");// 设置创建线程的优先级(默认为Thread.NORM_PRIORITY)//executor.setThreadPriority(Thread.NORM_PRIORITY);// 是否允许核心线程数超时(默认为false,设置为true后,即使队列未满,也可实现线程的动态增加和减少)//executor.setAllowCoreThreadTimeOut(true);return executor;
}

4.3 调用线程池

(1)无返回值

threadPoolTaskExecutor.execute(new Runnable() {@Overridepublic void run() {// 输出:common thread1System.out.println(Thread.currentThread().getName());}
});

(2)有返回值

Future<String> future = threadPoolTaskExecutor.submit(new Callable<String>() {@Overridepublic String call() throws Exception {return Thread.currentThread().getName();}
});
try {// 输出:common thread1System.out.println(future.get());
} catch (InterruptedException e) {// 线程被中断时抛出的异常e.printStackTrace();
} catch (ExecutionException e) {// Callable类中抛出的异常e.printStackTrace();
}

4.4 注解调用

调用spring中的线程池异步执行方法,也可通过spring中的注解@Async来实现,此注解可以加到方法上或者类上(加类上后,类上面的所有代理方法,均为异步线程方法)。

4.4.1 开启注解

(1)springBoot开启注解

通过在启动类上,加上@EnableAsync注解来开启@Async的异步线程方法。

(2)springMvc开启注解

sprignMvc通过加上如下配置,来开启@Async的异步线程方法:

<!-- executor为默认的spring线程池 -->
<task:annotation-driven executor="threadPoolTaskExecutor"/>
4.4.2 使用异步方法
@Async
public void testAsyncMethod() {// 输出:common thread1System.out.println(Thread.currentThread().getName());
}
4.4.3 多个线程池的情况

spring中也可以定义多个线程池,每个线程池的类型均为ThreadPoolTaskExecutor,每个线程池的name不同

@Async注解不加参数时,使用的为默认的线程池,如需要指定线程池,可根据bean的name来指定,如下:

(1)定义非默认线程池

@Bean
public ThreadPoolTaskExecutor otherThreadPoolTaskExecutor() {......
}

(2)调用非默认线程池

@Async(value = "otherThreadPoolTaskExecutor")
public void testAsyncMethod() {System.out.println(Thread.currentThread().getName());
}

注解的value注释说明:

指定的异步操作的限定符值。

可用于确定在执行异步操作时要使用的目标执行器,它与特定执行器或TaskExecutor bean定义的限定符值(或bean名称)匹配。

当在类级别@Async注释上指定时,指示给定的执行器应用于类中的所有方法。方法级使用Async#value总是覆盖类级别设置的任何值。

java中的多线程使用方式相关推荐

  1. 草根方式学习java中的多线程

    草根方式学习java中的多线程 下面有具体的代码和截图 源码点这里 多线程即在同一时间,可以做多件事情(说白了,就是齐头并进) 单线程就是按部就班 创建多线程有2种方式,分别是继承线程Thread类, ...

  2. java中实现多线程的三种方式

    java中实现多线程的三种方式 1.实现多线程的方法: 在java中实现多线程的两途径:继承Thread类,实现Runable接口(Callable) 2.继承Thread类实现多线程: ​ 继承类T ...

  3. Java中的多线程编程(超详细总结)

    文章目录 Java中的多线程编程(超详细总结) 一.线程与多线程的概念 二.线程与进程之间的关系 三.一个线程的生命周期 四.多线程的目的和意义 五.线程的实现的方式 Java中的多线程编程(超详细总 ...

  4. JAVA中的多线程(一)

    JAVA中的多线程(一) 进程:是一个正在执行中的程序 每一个进程执行都有一个执行的顺序,该顺序是一个执行路径,或者叫控制单元 线程:就是进程中的一个独立的控制单元 线程在控制着进程的执行 一个进程中 ...

  5. Java中的多线程基本介绍

    在 Java 中,多线程是指同时执行两个或多个线程以最大限度地利用 CPU 的过程. Java 中的线程是一个轻量级进程,只需要较少的资源即可创建和共享进程资源. 多线程和多进程用于 Java 中的多 ...

  6. Java基础——深入理解Java中的多线程(超级详细,值得你看)

    Java中的多线程 进程(process)是程序的一次执行过程,或是正在运行的有一个程序,或是正在运行的一个程序.是一个动态的过程:有它自身的产生.存在和消亡的过程.--生命周期. 线程(thread ...

  7. JAVA中实现多线程

    一,JAVA中实现多线程(一) 1,在Java中负责线程的这个功能的是Java.lang.Thread 这个类 2,可以通过创建 Thread 的实例来创建新的线程. 3,每个线程都是通过某个特定Th ...

  8. opengl中的Floatbuffer和IntBuffer与java中数据的存储方式不同的解决方法,编辑一个自己的BufferUtil工具类

    opengl中的Floatbuffer和IntBuffer与java中数据的存储方式不同的解决方法,编辑一个自己的BufferUtil工具类 参考文章: (1)opengl中的Floatbuffer和 ...

  9. JS 和 Java 中URL特殊字符编码方式

    前几天遇到url特殊字符编码的问题,在这里整理一下: JavaScript 1.  编码 escape(String) 其中某些字符被替换成了十六进制的转义序列. 解码 unescape(String ...

最新文章

  1. hdu 1598 find the most comfortable road
  2. she's the one
  3. 牛客挑战赛30 C 小G砍树 换根dp+组合
  4. JSch - Java Secure Channel : java 代码实现服务器远程操作
  5. 下如何查看mysql表单_Navicat 教程:如何进行表单查看
  6. 关于获取oracle中数据变更的时间戳的探索(待更新)
  7. 蓝桥杯单片机数码管动态显示_单片机静态动态数码管
  8. python基本判断语句_python两种简洁的条件判断语句写法
  9. 仿射解密c语言程序实验报告,仿射加密解密 - 依姆哣特的个人空间 - OSCHINA - 中文开源技术交流社区...
  10. base64图裁剪 php_3分钟短文 | 有挑战!PHP用1个函数实现post请求,你用哪个?
  11. 运筹优化(十二)--带约束非线性规划(NLP)
  12. 关于PPPOE拨号分配给用户32位掩码,且IP与网关相同的问题
  13. 64位的windows server 2003运行IIS6运行32位的.NET程序
  14. AD14,原理图绘制引脚以及引脚名称的修改
  15. 用java操作MySQL编写的高校水电费管理系统
  16. excel换行快捷键_超实用的16个Excel快捷键,一定要收藏!
  17. 领导力包括哪些能力?如何提升领导力?
  18. 解读ACL2020的一篇机器阅读理解方向的论文(Recurrent Chunking Mechanisms for Long-text machine reading comprehension)
  19. c#版汉字拼音大全,支持多音字
  20. 敏捷型程序员_组织敏捷程序:第2部分,用于管理敏捷程序的网络

热门文章

  1. numactl:NETLINK示例源码
  2. The Linux Storage Stack Diagram
  3. 《计算机网络》第四章:介质访问控制(The Medium Access Control Sublayer)
  4. C语言assert关键字
  5. Python项目实践:绘制玫瑰花
  6. lisp批量生成轴线_求批量插入图纸的程序 - AutoLISP/Visual LISP 编程技术 - CAD论坛 - 明经CAD社区 - Powered by Discuz!...
  7. 跨屏html ui,Amaze UI(HTML5 跨屏前端框架) v2.7.2
  8. mysql前两个月_MySQL数据库表始终保持最近两个月的记录
  9. c语言突然出现图片,c语言能显示图片吗
  10. 已有Unity工程升级到LWRP/HDRP后材质都变洋红色了,怎么办?