java基础:简单实现线程池
前段时间自己研究了下线程池的实现原理,通过一些源码对比,发现其实核心的东西不难,于是抽丝剥茧,决定自己实现一个简单线程池,当自已实现了出一个线程池后。发现原来那么高大上的东西也可以这么简单。
先上原理图:为了更好的在手机上显示,我重新把图画了一遍

上代码之前,要先补充一下线程池构造的核心几个点
- 线程池里的核心线程数与最大线程数
- 线程池里真正工作的线程
worker - 线程池里用来存取任务的队列
BlockingQueue - 线程中的任务
task
本例实现简化了一些,只实现了BlockingQueue存放任务,然后每个worker取任务并执行,下面看代码
首先定义一个线程池ThreadExcutor
class ThreadExcutor{
//创建
private volatile boolean RUNNING = true;
//所有任务都放队列中,让工作线程来消费
private static BlockingQueue<Runnable> queue = null;
private final HashSet<Worker> workers = new HashSet<Worker>();
private final List<Thread> threadList = new ArrayList<Thread>();
//工作线程数
int poolSize = 0;
//核心线程数(创建了多少个工作线程)
int coreSize = 0;
boolean shutdown = false;
public ThreadExcutor(int poolSize){
this.poolSize = poolSize;
queue = new LinkedBlockingQueue<Runnable>(poolSize);
}
public void exec(Runnable runnable) {
if (runnable == null) throw new NullPointerException();
if(coreSize < poolSize){
addThread(runnable);
}else{
//System.out.println("offer" + runnable.toString() + " " + queue.size());
try {
queue.put(runnable);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void addThread(Runnable runnable){
coreSize ++;
Worker worker = new Worker(runnable);
workers.add(worker);
Thread t = new Thread(worker);
threadList.add(t);
try {
t.start();
}catch (Exception e){
e.printStackTrace();
}
}
public void shutdown() {
RUNNING = false;
if(!workers.isEmpty()){
for (Worker worker : workers){
worker.interruptIfIdle();
}
}
shutdown = true;
Thread.currentThread().interrupt();
}
//这里留个位置放内部类Worker
}
然后定义一个内部类Worker,这个内部类Worker是用来执行每个任务的,在创建线程池后,往线程里添加任务,每个任务都是由Worker一个一个来启动的。
/**
* 工作线程
*/
class Worker implements Runnable{
public Worker(Runnable runnable){
queue.offer(runnable);
}
@Override
public void run() {
while (true && RUNNING){
if(shutdown == true){
Thread.interrupted();
}
Runnable task = null;
try {
task = getTask();
task.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public Runnable getTask() throws InterruptedException {
return queue.take();
}
public void interruptIfIdle() {
for (Thread thread :threadList) {
System.out.println(thread.getName() + " interrupt");
thread.interrupt();
}
}
}
首先注意的一点,这个Worker是个内部类,是在线程池内声明的。
exec方法

Worker怎么工作

这个工作线程实例化的时候就先加入一个任务到队列中,也就是说在实例化这个工作线程时,这个工作线程也是一个任务被加入到线程池中。然后就是run方法,这个run方法是线程调start方法生成的线程,而Worker调的run方法并没有生成新的线程。就是一个循环,一直在不停的从队列中取任务,然后执行。可以看到,取队列的方法是take(),这个方法意思如果队列为空了,取不到数据时就阻塞队列。
然后看shutdown()

你每天辛勤的劳动着,突然接收到上面的命令,说活暂时不要接了,先停下来,当你还没搞清楚状况时,接着你的领导又把你开除了,说公司要倒了,你先下岗吧,一会我也得下岗了。这就是shutdown做的事,shutdown必须是主线程才能停止工作线程。
shutdown方法并不是用线程那种强制停止的搞法,而是先用一个标识符告诉工作线程,不要再接任务了。然后通知工作线程,你可以interrupt()了,当所有的线程停止后记得要把主线程也停掉,这样,一个简单任务的线程池就完成了。
让我们来测试一下:
/**
* Created by wxwall on 2017/6/7.
*/
public class TheadBlockedQ {
public static void main(String[] args) throws InterruptedException {
ThreadExcutor excutor = new ThreadExcutor(3);
for (int i = 0; i < 10; i++) {
excutor.exec(new Runnable() {
@Override
public void run() {
System.out.println("线程 " + Thread.currentThread().getName() + " 在帮我干活");
}
});
}
excutor.shutdown();
}
}
输出结果为:
线程 Thread-0 在帮我干活
线程 Thread-2 在帮我干活
线程 Thread-1 在帮我干活
线程 Thread-0 在帮我干活
线程 Thread-2 在帮我干活
线程 Thread-2 在帮我干活
线程 Thread-1 在帮我干活
线程 Thread-0 在帮我干活
Thread-0 interrupt
Thread-1 interrupt
Thread-2 interrupt
Thread-0 interrupt
Thread-1 interrupt
Thread-2 interrupt
Thread-0 interrupt
Thread-1 interrupt
Thread-2 interrupt
这当然是最简单实现,JDK的实现比这强大的多,而且还具备当工作线程处理不过来时,可以产生新的线程来处理任务,这个数量不能超过原先定义的最大线程数,而在本例中都没实现这些功能。
我相信当想了解一个模块的功能时,如果一开始就了解其中最核心的点,然后向外慢慢扩展,那么学习这个模块时一定能省下不少时间,而且理解将很深刻。希望这个简单线程池实现能让你有所领悟,以更加简单的方式了解线程池,了解了线程池,对于其他池化技术,原理都是相通的。
最后我想说:我相信写好一篇文章能让大家理解不困难都是用了心的,您也点个赞支持支持。
java基础:简单实现线程池的更多相关文章
- JAVA基础拾遗-论线程池的线程粒度划分与深浅放置
摘要:多线程任务处理对提高性能很有帮助,在Java中提供的线程池也方便了对多线程任务的实现.使用它很简单,而如果进行了不正确的使用,那么代码将陷入一团乱麻.因此如何正确地使用它,如以下分享,这个技能你 ...
- Java 基础 多线程和线程池基础
一,多线程 1.1 多线程介绍 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线程:线程是进程中的一个执行单元,负 ...
- Java一个简单的线程池实现
线程池代码 import java.util.List; import java.util.Vector; public class ThreadPool { private static ...
- Java多线程系列--“JUC线程池”04之 线程池原理(三)
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...
- 简单C++线程池
简单C++线程池 Java 中有一个很方便的 ThreadPoolExecutor,可以用做线程池.想找一下 C++ 的类似设施,尤其是能方便理解底层原理可上手的.网上找到的 demo,基本都是介绍的 ...
- Java并发编程:线程池的使用
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- Java多线程系列--“JUC线程池”03之 线程池原理(二)
概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...
- [转] 引用 Java自带的线程池ThreadPoolExecutor详细介绍说明和实例应用
PS: Spring ThreadPoolTaskExecutor vs Java Executorservice cachedthreadpool 引用 [轰隆隆] 的 Java自带的线程池Thre ...
- Java并发编程:线程池的使用(转)
Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...
- Java视频扩展知识 线程池的了解
Java视频扩展知识 线程池的了解 1.简单介绍: Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用.为我们在开发中处理线程的 ...
随机推荐
- JavaScript中apply与call方法
一.定义 apply:应用某一对象的一个方法,用另一个对象替换当前对象. call:调用一个对象的一个方法,以另一个对象替换当前对象. 二.apply //apply function Person( ...
- [移动端] IOS下border-image不起作用的解决办法
上周五突然接到现场的一个需求,做一个移动端的劳模展示页面.现场美工把原型图发了过来.这个样子的: 说实在的很想吐槽一下我们美工的审美哈,不过这不是重点. 因为边框是需要特殊花纹的所以打算用border ...
- [UWP]了解模板化控件(8):ItemsControl
1. 模仿ItemsControl 顾名思义,ItemsControl是展示一组数据的控件,它是UWP UI系统中最重要的控件之一,和展示单一数据的ContentControl构成了UWP UI的绝大 ...
- Codeforces Round #410 (Div. 2)C题
C. Mike and gcd problem time limit per test 2 seconds memory limit per test 256 megabytes input stan ...
- Spring3.0配置多个事务管理器(即操作多个数据源)的方法
大多数项目只需要一个事务管理器.然而,有些项目为了提高效率.或者有多个完全不同又不相干的数据源,最好用多个事务管理器.机智的Spring的Transactional管理已经考虑到了这一点,首先分别定义 ...
- MySQL ProxySQL读写分离实践
目的 在上一篇文章MySQL ProxySQL读写分离使用初探里初步介绍了ProxySQL的使用,本文继续介绍它的一些特点和DBProxy的性能差异.深入一些去了解ProxySQL,通过测试来说明Pr ...
- angular页面缓存与页面刷新
angularJS学习笔记:页面缓存与页面刷新 遇到的问题 现在存在这样一个问题,登录前与登录成功后是同一个页面,只不过通过ngIf来控制哪部分显示,图像信息如下: 所以,整体工作不是很难,无非就 ...
- SSH抛出org.apache.ibatis.exceptions.PersistenceException: 异常
抛出的异常类容如下 如果遇到这个异常,那么肯定是你在配置事物切面时出错,或者是你的写的事物的方法名称没有和这里的配置对应: 你需要注意如下几点: 1.你的名称必须是以英文开头 2.在你用着事物方法的名 ...
- 【WPF】三维模型中的“照相机”
WPF 部分支持三维模型,为啥说是部分支持?毕竟 WPF 的侧重点还是在应用开发上,虽然也有些游戏是用 WPF 开发的,不过,老周想啊,如果真要开发游戏,最好用专门的框架,WPF 应当用于开发应用功能 ...
- @JsonFormat 日期格式自动格式化
通常日期格式都是以时间戳的形式存放在数据库里,当前端页面通过接口查询时,我们会将一个对象的某些属性查出来返回给页面. 例如,某个类里面有个属性 Timestamp create_time 给这个对象实 ...