浅谈Java 线程池原理及使用方式
一、简介
- 什么是线程池?
池的概念大家也许都有所听闻,池就是相当于一个容器,里面有许许多多的东西你可以即拿即用。java中有线程池、连接池等等。线程池就是在系统启动或者实例化池时创建一些空闲的线程,等待工作调度,执行完任务后,线程并不会立即被销毁,而是重新处于空闲状态,等待下一次调度。
- 线程池的工作机制?
在线程池的编程模式中,任务提交并不是直接提交给线程,而是提交给池。线程池在拿到任务之后,就会寻找有没有空闲的线程,有则分配给空闲线程执行,暂时没有则会进入等待队列,继续等待空闲线程。如果超出最大接受的工作数量,则会触发线程池的拒绝策略。
- 为什么使用线程池?
线程的创建与销毁需要消耗大量资源,重复的创建与销毁明显不必要。而且池的好处就是响应快,需要的时候自取,就不会存在等待创建的时间。线程池可以很好地管理系统内部的线程,如数量以及调度。
二、常用线程池介绍
Java类ExecutorService是线程池的父接口,并非顶层接口。以下四种常用线程池的类型都可以是ExecutorService。
- 单一线程池 Executors.newSingleThreadExecutor()
内部只有唯一一个线程进行工作调度,可以保证任务的执行顺序(FIFO,LIFO)
package com.test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PoolTest {
public static void main(String[] args) {
// 创建单一线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
List<String> list = new ArrayList<String>();
list.add("first");
list.add("second");
list.add("third");
list.forEach(o -> {
// 遍历集合提交任务
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " : " + o);
try {
// 间隔1s
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
});
}
}
执行结果:
pool-1-thread-1 : first
pool-1-thread-1 : second
pool-1-thread-1 : third
- 可缓存线程池 Executors.newCachedThreadPool()
如果线程池中有可使用的线程,则使用,如果没有,则在池中新建一个线程,可缓存线程池中线程数量最大为Integer.MAX_VALUE。通常用它来运行一些执行时间短,且经常用到的任务。
package com.test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PoolTest {
public static void main(String[] args) {
// 创建可缓存线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
List<String> list = new ArrayList<String>();
list.add("first");
list.add("second");
list.add("third");
list.forEach(o -> {
try {
// 间隔3s
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 遍历集合提交任务
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " : " + o);
try {
// 间隔1s
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
});
}
}
执行结果:
pool-1-thread-1 : first
pool-1-thread-1 : second
pool-1-thread-1 : third
因为间隔时间长,下一个任务运行时,上一个任务已经完成,所以线程可以继续复用,如果间隔时间调短,那么部分线程将会使用新线程来运行。
把每个任务等待时间从3s调低至1s:
执行结果:
pool-1-thread-1 : first
pool-1-thread-2 : second
pool-1-thread-1 : third
- 定长线程池 Executors.newFixedThreadPool(int nThreads)
创建一个固定线程数量的线程池,参数手动传入
package com.test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PoolTest {
public static void main(String[] args) {
// 创建可缓存线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
List<String> list = new ArrayList<String>();
list.add("first");
list.add("second");
list.add("third");
list.add("fourth");
list.forEach(o -> {
try {
// 间隔1s
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 遍历集合提交任务
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " : " + o);
try {
// 间隔1s
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
});
}
}
执行结果:
pool-1-thread-1 : first
pool-1-thread-2 : second
pool-1-thread-3 : third
pool-1-thread-1 : fourth
- 定时线程池 Executors.newScheduledThreadPool(int corePoolSize)
创建一个定长线程池,支持定时及周期性任务执行
package com.test;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class PoolTest {
public static void main(String[] args) {
// 创建定长线程池、支持定时、延迟、周期性执行任务
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " : 1秒后每隔3秒执行一次");
}
}, 1, 3, TimeUnit.SECONDS);
}
}
执行结果:
pool-1-thread-1 : 1秒后每隔3秒执行一次
pool-1-thread-1 : 1秒后每隔3秒执行一次
pool-1-thread-2 : 1秒后每隔3秒执行一次
pool-1-thread-2 : 1秒后每隔3秒执行一次
pool-1-thread-2 : 1秒后每隔3秒执行一次
pool-1-thread-2 : 1秒后每隔3秒执行一次
pool-1-thread-2 : 1秒后每隔3秒执行一次
三、自定义线程池
常用构造函数:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
参数说明:
1、corePoolSize 核心线程数大小,当线程数<corePoolSize ,会创建线程执行runnable
2、maximumPoolSize 最大线程数, 当线程数 >= corePoolSize的时候,会把runnable放入workQueue中
3、keepAliveTime 保持存活时间,当线程数大于corePoolSize的空闲线程能保持的最大时间。
4、unit 时间单位
5、workQueue 保存任务的阻塞队列
6、threadFactory 创建线程的工厂
7、handler 拒绝策略
任务执行顺序:
1、当线程数小于corePoolSize时,创建线程执行任务。
2、当线程数大于等于corePoolSize并且workQueue没有满时,放入workQueue中
3、线程数大于等于corePoolSize并且当workQueue满时,新任务新建线程运行,线程总数要小于maximumPoolSize
4、当线程总数等于maximumPoolSize并且workQueue满了的时候执行handler的rejectedExecution。也就是拒绝策略。
ThreadPoolExecutor默认有四个拒绝策略:
1、new ThreadPoolExecutor.AbortPolicy() 直接抛出异常RejectedExecutionException
2、new ThreadPoolExecutor.CallerRunsPolicy() 直接调用run方法并且阻塞执行
3、new ThreadPoolExecutor.DiscardPolicy() 直接丢弃后来的任务
4、new ThreadPoolExecutor.DiscardOldestPolicy() 丢弃在队列中队首的任务
缓冲队列BlockingQueue:
BlockingQueue是双缓冲队列。BlockingQueue内部使用两条队列,允许两个线程同时向队列一个存储,一个取出操作。在保证并发安全的同时,提高了队列的存取效率。
常用的几种BlockingQueue:
ArrayBlockingQueue(int i):规定大小的BlockingQueue,其构造必须指定大小。其所含的对象是FIFO顺序排序的。
LinkedBlockingQueue()或者(int i):大小不固定的BlockingQueue,若其构造时指定大小,生成的BlockingQueue有大小限制,不指定大小,其大小有Integer.MAX_VALUE来决定。其所含的对象是FIFO顺序排序的。
PriorityBlockingQueue()或者(int i):类似于LinkedBlockingQueue,但是其所含对象的排序不是FIFO,而是依据对象的自然顺序或者构造函数的Comparator决定。
SynchronizedQueue():特殊的BlockingQueue,对其的操作必须是放和取交替完成。
package com.test;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class PoolTest {
public static void main(String[] args) {
// 工作队列
LinkedBlockingDeque<Runnable> workQueue = new LinkedBlockingDeque<Runnable>();
// 拒绝策略
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 10, 20, TimeUnit.MILLISECONDS, workQueue, handler);
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println("自定义线程池");
}
});
}
}
浅谈Java 线程池原理及使用方式的更多相关文章
- 浅谈java线程池实现
再进入主题之前,我们先了解几个概念,对读源码有所帮助,对于线程池的运行状态,有4个级别,分别是RUNNING,SHUTING,STOP,TIDING,TERMINATED 解释如下: The runS ...
- java线程池原理及实现方式
线程池的定义 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程 为什么要使用线程池 1.减少在创建和销毁线程上所花的时间以及系统资源的开 ...
- 浅谈Java线程安全
浅谈Java线程安全 - - 2019-04-25 17:37:28 线程安全 Java中的线程安全 按照线程安全的安全程序由强至弱来排序,我们可以将Java语言中各种操作共享的数据分为以下五类 ...
- 转载【浅谈ThreadPool 线程池】
浅谈ThreadPool 线程池 http://www.cnblogs.com/xugang/archive/2010/04/20/1716042.html
- 浅谈ThreadPool 线程池(引用)
出自:http://www.cnblogs.com/xugang/archive/2010/04/20/1716042.html 浅谈ThreadPool 线程池 相关概念: 线程池可以看做容纳线程的 ...
- Java 线程池原理分析
1.简介 线程池可以简单看做是一组线程的集合,通过使用线程池,我们可以方便的复用线程,避免了频繁创建和销毁线程所带来的开销.在应用上,线程池可应用在后端相关服务中.比如 Web 服务器,数据库服务器等 ...
- java线程池原理
在什么情况下使用线程池? 1.单个任务处理的时间比较短 2.将需处理的任务的数量大 使用线程池的好处: 1.减少在创建和销毁线程上所花的时间以及系统资源的开销 ...
- Java线程池原理解读
引言 引用自<阿里巴巴JAVA开发手册> [强制]线程资源必须通过线程池提供,不允许在应用中自行显式创建线程. 说明:使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销 ...
- 含源码解析,深入Java 线程池原理
从池化技术到底层实现,一篇文章带你贯通线程池技术. 1.池化技术简介 在系统开发过程中,我们经常会用到池化技术来减少系统消耗,提升系统性能. 在编程领域,比较典型的池化技术有: 线程池.连接池.内存池 ...
随机推荐
- C#算法设计排序篇之05-归并排序(附带动画演示程序)
归并排序(Merge Sort) 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/683 访问. 归并排序是建立在归并操作 ...
- Spring Boot自定义错误视图
Spring Boot缺省错误视图解析器 Web应用在处理请求的过程中发生错误是非常常见的情况,SpringBoot中为我们实现了一个错误视图解析器(DefaultErrorViewResolver) ...
- 代码备忘录--常用的一些Doxygen格式
1.文件头的格式: /** **************************************************************************** * @file x ...
- Kafka 为什么快
Kafka 为什么能那么快 | Kafka高效读写数据的原因 无论 kafka 作为 MQ 也好,作为存储层也罢,无非就是两个功能(好简单的样子),一是 Producer 生产的数据存到 broker ...
- CentOS7 安装图形化桌面
1.装好CentOS7后,我们一开始是上不了网的 (ping 百度报错:Name or service not know) 2.输入dhclient,可以自动获取一个IP地址,再用命令ip addr查 ...
- 自我介绍网页填写表格PHP,JavaScript,html,css代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Spring MVC 的运行流程
1.用户发送请求到DispatcherServlet 2.DispatcherServlet调用处理器映射器(HanderMapping)找到处理器 3.处理器映射器(HanderMapping)返回 ...
- 第1篇 Scrum 冲刺博客
每个成员在Alpha阶段认领的任务 钟智锋 内容 工时 客户端请求对局 1h 客户端请求移动/使用锦囊/技能/进化 1h 把对局信息转换成可传播的信息 2h 客户端通信 3h 编写博客和文档 1h 岑 ...
- Java并发---concurrent包
一.包的结构层次 其中包含了两个子包atomic和locks,另外字concurrent下的阻塞队列以及executor,这些就是concurrent包中的精华.而这些类的实现主要是依赖于volati ...
- Java数据结构——图的基本理论及简单实现
1. 图的定义图(graph)是由一些点(vertex)和这些点之间的连线(edge)所组成的:其中,点通常被成为"顶点(vertex)",而点与点之间的连线则被成为"边 ...