使用多线程以及线程池的意义无需多说,要想掌握线程池,最好的方法还是自己手动去实现。

一、实现思路

                      (网络盗图)

二、实现代码

1、线程池类

package com.ty.thread;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; /**
* @author Taoyong
* @date 2018年5月17日
* 天下没有难敲的代码!
*/
public class ThreadPoolExecutor { /*
* BlockingQueue是阻塞队列,在两种情况下出现阻塞:
* 1、当队列满了,入队列操作时;
* 2、当队列空了,出队列操作时。
* 阻塞队列是线程安全的,主要使用在生产/消费者的场景
*/
private BlockingQueue<Task> blockingQueue; //线程池的工作线程数(可以认为是线程池的容量)
private int poolSize = 0; //线程池的核心容量(也就是当前线程池中真正存在的线程个数)
private int coreSize = 0; /*
* 此地方使用volatile关键字,volatile的工作原理是:对于JVM维度来说,每个线程持有变量的工作副本,那对于计算机维度来说,
* 就是这些变量的中间值会存放在高速缓存中。通过volatile关键字,告知每个线程改变此变量之后,立马更新到内存中去,并且使得
* 缓存中的数据失效,这样来保证其中某个线程改变公有变量后,其他线程能及时读取到最新的变量值,从而保证可见性。
* 原因如下:
* 1、在ThreadPoolExecutorTest中操作shutDown,这是main线程操作此变量(由于变量是volatile声明,所以会立马写入内存中);
* 2、Worker中线程通过while(!shutDown)来判断当前线程是否应该关闭,因此需通过volatile保证可见性,使线程可以及时得到关闭。
*/
private volatile boolean shutDown = false; public ThreadPoolExecutor(int size) {
this.poolSize = size;
//LinkedBlockingQueue的大小可以指定,不指定即为无边界的。
blockingQueue = new LinkedBlockingQueue<>(poolSize);
} public void execute(Task task) throws InterruptedException {
if(shutDown == true) {
return;
} if(coreSize < poolSize) {
/*
* BlockingQueue中的插入主要有offer(obj)以及put(obj)两个方法,其中put(obj)是阻塞方法,如果插入不能马上进行,
* 则操作阻塞;offer(obj)则是插入不能马上进行,返回true或false。
* 本例中的Task不允许丢失,所以采用put(obj);
*/
blockingQueue.put(task);
produceWorker(task);
}else {
blockingQueue.put(task);
}
} private void produceWorker(Task task) throws InterruptedException {
if(task == null) {
throw new NullPointerException("非法参数:传入的task对象为空!");
} Thread thread = new Thread(new Worker());
thread.start();
coreSize++;
} /*
* 真正中断线程的方法,是使用共享变量发出信号,告诉线程停止运行。
*
*/
public void shutDown() {
shutDown = true;
} /*
* 此内部类是实际上的工作线程
*
*/
class Worker implements Runnable { @Override
public void run() {
while(!shutDown) {
try {
//
blockingQueue.take().doJob();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程:" + Thread.currentThread().getName() + "退出运行!");
}
}
}

2、Task类(需要被线程处理的任务类)

package com.ty.thread;

/**
* @author Taoyong
* @date 2018年5月17日
* 天下没有难敲的代码!
*/
public class Task { //通过taskId对任务进行标识
private int taskId; public Task(int taskId) {
this.taskId = taskId;
} public void doJob() {
System.out.println("线程" + Thread.currentThread().getName() + "正在处理任务!");
} public int getId() {
return taskId;
}
}

3、测试类

package com.ty.thread;

/**
* @author Taoyong
* @date 2018年5月17日
* 天下没有难敲的代码!
*/
public class ThreadPoolExecutorTest { public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3);
for(int i = 0; i < 10; i++) {
Task task = new Task(i);
threadPoolExecutor.execute(task);
} threadPoolExecutor.shutDown();
}
}

4、运行结果

线程Thread-0正在处理任务!
线程Thread-1正在处理任务!
线程Thread-0正在处理任务!
线程Thread-1正在处理任务!
线程Thread-2正在处理任务!
线程Thread-0正在处理任务!
线程Thread-1正在处理任务!
线程:Thread-1退出运行!
线程:Thread-0退出运行!
线程Thread-2正在处理任务!
线程:Thread-2退出运行!

当第十个任务待处理时,整个线程池已经被shutDown,整个流程结束。

项目代码已经上传到github中:https://github.com/ali-mayun/threadPool

简单实现java线程池的更多相关文章

  1. 这么说吧,java线程池的实现原理其实很简单

    好处 : 线程是稀缺资源,如果被无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,合理的使用线程池对线程进行统一分配.调优和监控,有以下好处: 1.降低资源消耗: 2.提高响应速度: 3.提高线 ...

  2. java线程池ThreadPoolExecutor理解

    Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.newFixe ...

  3. Java线程池使用说明

    Java线程池使用说明 转自:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极 ...

  4. Java线程池的几种实现 及 常见问题讲解

    工作中,经常会涉及到线程.比如有些任务,经常会交与线程去异步执行.抑或服务端程序为每个请求单独建立一个线程处理任务.线程之外的,比如我们用的数据库连接.这些创建销毁或者打开关闭的操作,非常影响系统性能 ...

  5. Java线程池的原理及几类线程池的介绍

    刚刚研究了一下线程池,如果有不足之处,请大家不吝赐教,大家共同学习.共同交流. 在什么情况下使用线程池? 单个任务处理的时间比较短 将需处理的任务的数量大 使用线程池的好处: 减少在创建和销毁线程上所 ...

  6. [转 ]-- Java线程池使用说明

    Java线程池使用说明 原文地址:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1. ...

  7. java线程池的使用与详解

    java线程池的使用与详解 [转载]本文转载自两篇博文:  1.Java并发编程:线程池的使用:http://www.cnblogs.com/dolphin0520/p/3932921.html   ...

  8. java线程池分析和应用

    比较 在前面的一些文章里,我们已经讨论了手工创建和管理线程.在实际应用中我们有的时候也会经常听到线程池这个概念.在这里,我们可以先针对手工创建管理线程和通过线程池来管理做一个比较.通常,我们如果手工创 ...

  9. Java线程池使用和分析(二) - execute()原理

    相关文章目录: Java线程池使用和分析(一) Java线程池使用和分析(二) - execute()原理 execute()是 java.util.concurrent.Executor接口中唯一的 ...

随机推荐

  1. Codeforces Beta Round #49 (Div. 2)

    Codeforces Beta Round #49 (Div. 2) http://codeforces.com/contest/53 A #include<bits/stdc++.h> ...

  2. 微信小程序开发——打开另一个小程序

    微信小程序打开另一个小程序,有两种方法:1.超链接:2.点击按钮. 全局配置: 跳转到其他小程序,需要在当前小程序全局配置中配置需要跳转的小程序列表,代码如下: App.json { ... &quo ...

  3. “东信杯”广西大学第一届程序设计竞赛(同步赛)H

    链接:https://ac.nowcoder.com/acm/contest/283/H来源:牛客网 题目描述 由于临近广西大学建校90周年校庆,西大开始了喜闻乐见的校园修缮工程! 然后问题出现了,西 ...

  4. jQuery与js对象互转

    @参考文章 1 jQuery对象转js 亲测有效 var $v=$("#v"); //jQuery对象 ); //js对象 或 var $v =$("#v") ...

  5. nginx配置websocket

    有时候我们需要给websocket服务端做一下nginx的配置,比如需要给websocket服务端做负载均衡,或者,有些系统要求访问websocket的时候不能带端口,这时候我们就需要用nginx来进 ...

  6. call指令和ret指令配合实现子程序调用

    子程序的框架如下. 标号: 指令 ret 具有子程序的源程序的框架如下. assume cs:code code segment main: : : call sub1 : : mov ax,4c00 ...

  7. A Spectral Technique for Correspondence Problems Using Pairwise Constraints

    Abstract 我们提出了一种有效的谱方法来寻找两组特征之间的一致对应关系.我们建立了一个图的邻接矩阵M,它的节点代表了潜在的对应,而链接上的权重代表潜在的对应之间的成对协议.正确的分配可在彼此之间 ...

  8. golang语言中os/signal包的学习与使用

    package main; import ( "os" "os/signal" "fmt" ) //signal包中提供了两个函数 //No ...

  9. BitCoinCore配置文件解读

    bitcoin.conf 配置文件 除了 -datadir 和 -conf 以外的所有命令行参数都可以通过一个配置文件来设置,而所有配置文件中的选项也都可以在命令行中设置.命令行参数设置的值会覆盖配置 ...

  10. Repeater控件添加序号列

    在项目开发过程中,会经常遇到ASP.NET repeater控件添加序号列,有些新手可能还不会,网上搜集整理了一些,需要的朋友可以参考下 ASP.NET repeater添加序号列的方法 1.< ...