Java 线程池的原理及实现
1、线程池简介:
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。
如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。
一个线程池包括以下四个基本组成部分:
1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目,看一个例子:
假设一个服务器一天要处理50000个请求,并且每个请求需要一个单独的线程完成。在线程池中,线程数一般是固定的,所以产生线程总数不会超过线程池中线程的数目,而如果服务器不利用线程池来处理这些请求则线程总数为50000。一般线程池大小是远小于50000。所以利用线程池的服务器程序不会为了创建50000而在处理请求时浪费时间,从而提高效率。
线程池实现代码:
- package com.wb.thread;
- import java.util.LinkedList;
- import java.util.List;
- /**
- * 线程池类
- * @author wangbo
- *
- */
- public class ThreadPool {
- private static int worker_num = 5;//线程池中线程的个数,默认为5
- private WorkThread[] workthreads;//工作线程
- private static volatile int finished_task = 0;//未处理的任务
- private List<Runnable> taskQueue = new LinkedList<Runnable>();//任务队列
- private static ThreadPool threadPool;
- /**
- * 无参构造器,创建线程池
- */
- private ThreadPool(){
- this(5);
- }
- /**
- * 含参构造器,创建线程池
- * @param num
- */
- private ThreadPool(int num){
- worker_num = num;
- workthreads = new WorkThread[num];
- for (int i = 0; i < workthreads.length; i++) {
- workthreads[i] = new WorkThread();
- workthreads[i].start();//开启线程
- }
- }
- /**
- * 获得一个默认线程个数的线程池
- * @return
- */
- public static ThreadPool getThreadPool(){
- return getThreadPool(ThreadPool.worker_num);
- }
- /**
- * 获得一个指定线程个数的线程池
- * @param num
- * @return
- */
- public static ThreadPool getThreadPool(int num) {
- if (num <= 0) {
- num = ThreadPool.worker_num;
- }
- if (threadPool == null) {
- threadPool = new ThreadPool(num);
- }
- return threadPool;
- }
- /**
- * 将任务单个添加到队列
- * @param task
- */
- public void execute(Runnable task){
- synchronized (taskQueue) {
- taskQueue.add(task);
- taskQueue.notify();
- }
- }
- /**
- * 将任务批量添加到队列
- * @param tasks
- */
- public void execute(Runnable[] tasks){
- synchronized (taskQueue) {
- for (Runnable runnable : tasks) {
- taskQueue.add(runnable);
- }
- taskQueue.notify();
- }
- }
- /**
- * 将任务批量添加到队列
- * @param tasks
- */
- public void execute(List<Runnable> tasks){
- synchronized (taskQueue) {
- for (Runnable runnable : tasks) {
- taskQueue.add(runnable);
- }
- taskQueue.notify();
- }
- }
- /**
- * 销毁线程池
- */
- public void destroy(){
- //还有任务没有执行完
- while(!taskQueue.isEmpty()){
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- //停止工作线程,且置为null
- for (int i = 0; i < workthreads.length; i++) {
- workthreads[i].stopWorker();
- workthreads[i] = null;
- }
- threadPool = null;
- taskQueue.clear();//清空队列
- }
- /**
- * 获取工作线程的个数
- * @return
- */
- public int getWorkThreadNumber(){
- return worker_num;
- }
- /**
- * 获取已完成任务数量
- * @return
- */
- public int getFinishedTaskNumber(){
- return finished_task;
- }
- /**
- * 获取未完成任务数量
- * @return
- */
- public int getWaitTaskNumber(){
- return taskQueue.size();
- }
- /**
- * 获取线程池信息
- */
- @Override
- public String toString() {
- return "工作线程数量:" + getWorkThreadNumber()
- + ",已完成任务数量" + getFinishedTaskNumber()
- + ",未完成任务数量" + getWaitTaskNumber();
- }
- /**
- * 内部类,工作线程
- * @author wangbo
- *
- */
- private class WorkThread extends Thread{
- private boolean isRunning = true;//线程有效标志
- @Override
- public void run() {
- Runnable runnable = null;
- while (isRunning) {
- synchronized (taskQueue) {
- //队列为空
- while (isRunning && taskQueue.isEmpty()) {
- try {
- taskQueue.wait(20);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- //队列不为空
- if (!taskQueue.isEmpty()) {
- runnable = taskQueue.remove(0);//去除任务
- }
- }
- if (runnable != null) {
- runnable.run();//执行任务
- }
- finished_task++;
- runnable = null;
- }
- }
- /**
- * 停止线程
- */
- public void stopWorker() {
- isRunning = false;
- }
- }
- }
测试代码:
- package com.wb.thread;
- public class ThreadPoolTest {
- public static void main(String[] args) {
- // 创建3个线程的线程池
- ThreadPool t = ThreadPool.getThreadPool(3);
- t.execute(new Runnable[] { new Task(), new Task(), new Task() });
- t.execute(new Runnable[] { new Task(), new Task(), new Task() });
- System.out.println(t);
- t.destroy();//所有线程都执行完成才destory
- System.out.println(t);
- }
- // 任务类
- static class Task implements Runnable {
- private static volatile int i = 1;
- @Override
- public void run() {// 执行任务
- System.out.println("任务 " + (i++) + " 完成");
- }
- }
- }
2、java类库中提供的线程池简介:
java.util.concurrent包提供了现成的线程池的实现。
示例代码:
- package com.wb.thread;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- /**
- * newCachedThreadPool()
- * 线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
- * 有任务才会创建线程,空闲线程会被保留60s
- * @author wangbo
- *
- */
- public class ThreadPoolExecutorTest1 {
- public static void main(String[] args) {
- ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
- for (int i = 0; i < 10; i++) {
- final int index = i;
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- cachedThreadPool.execute(new Runnable() {
- @Override
- public void run() {
- System.out.println(index);
- System.out.println(Thread.currentThread().getName());
- }
- });
- }
- }
- }
- package com.wb.thread;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- /**
- * newFixedThreadPool(int nThreads)
- * 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
- * 线程池中包含固定数目的线程,空闲线程会一直保留,参数nThreads表示设定线程池中线程的数目
- * @author wangbo
- *
- */
- public class ThreadPoolExecutorTest2 {
- public static void main(String[] args) {
- ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
- for (int i = 0; i < 10; i++) {
- final int index = i;
- fixedThreadPool.execute(new Runnable() {
- @Override
- public void run() {
- try {
- System.out.println(index);
- System.out.println(Thread.currentThread().getName());
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- });
- }
- }
- }
- package com.wb.thread;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.concurrent.Executors;
- import java.util.concurrent.ScheduledExecutorService;
- import java.util.concurrent.TimeUnit;
- /**
- * newScheduledThreadPool(int corePoolSize)
- * 线程池能按时间计划来执行任务,允许用户设定计划执行任务的时间。
- * 参数corePoolSize设定线程池中线程最小数目,当任务较多时,线程池可能会创建更多的工作线程来执行任务。
- * @author wangbo
- *
- */
- public class ThreadPoolExecutorTest3 {
- public static void main(String[] args) {
- method1();
- method2();
- }
- /**
- * 延迟3s执行
- */
- private static void method1(){
- System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
- ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
- scheduledThreadPool.schedule(new Runnable() {
- public void run() {
- System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
- System.out.println("延迟2s执行");
- }
- }, 2, TimeUnit.SECONDS);
- }
- /**
- * 延迟2s执行后每3s执行一次
- */
- private static void method2() {
- System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
- ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
- scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
- public void run() {
- System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
- System.out.println("延迟2s执行后每3s执行一次");
- System.out.println(Thread.currentThread().getName());
- }
- }, 2, 3, TimeUnit.SECONDS);
- }
- }
- package com.wb.thread;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- /**
- * newSingleThreadExecutor(int nThreads)
- * 线程池中只有一个线程,它依次执行每个任务。
- * 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
- * @author wangbo
- *
- */
- public class ThreadPoolExecutorTest4 {
- public static void main(String[] args) {
- ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
- for (int i = 0; i < 10; i++) {
- final int index = i;
- singleThreadPool.execute(new Runnable() {
- @Override
- public void run() {
- try {
- System.out.println(index);
- System.out.println(Thread.currentThread().getName());
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- });
- }
- }
- }
- package com.wb.thread;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.concurrent.Executors;
- import java.util.concurrent.ScheduledExecutorService;
- import java.util.concurrent.TimeUnit;
- /**
- * newSingleThreadScheduledExecutor()
- * 线程池中只有一个线程,它能按照时间计划执行每个任务。
- * @author wangbo
- *
- */
- public class ThreadPoolExecutorTest5 {
- public static void main(String[] args) {
- method1();
- method2();
- }
- /**
- * 延迟3s执行
- */
- private static void method1(){
- System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
- ScheduledExecutorService scheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
- scheduledThreadPool.schedule(new Runnable() {
- public void run() {
- System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
- System.out.println("延迟2s执行");
- }
- }, 2, TimeUnit.SECONDS);
- }
- /**
- * 延迟2s执行后每3s执行一次
- */
- private static void method2() {
- System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
- ScheduledExecutorService scheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
- scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
- public void run() {
- System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
- System.out.println("延迟2s执行后每3s执行一次");
- System.out.println(Thread.currentThread().getName());
- }
- }, 2, 3, TimeUnit.SECONDS);
- }
- }
Java 线程池的原理及实现的更多相关文章
- Java线程池的原理及几类线程池的介绍
刚刚研究了一下线程池,如果有不足之处,请大家不吝赐教,大家共同学习.共同交流. 在什么情况下使用线程池? 单个任务处理的时间比较短 将需处理的任务的数量大 使用线程池的好处: 减少在创建和销毁线程上所 ...
- 并发编程(十二)—— Java 线程池 实现原理与源码深度解析 之 submit 方法 (二)
在上一篇<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中提到了线程池ThreadPoolExecutor的原理以及它的execute方法.这篇文章是接着上一篇文章 ...
- Java 线程池的原理与实现 (转)
最近在学习线程池.内存控制等关于提高程序运行性能方面的编程技术,在网上看到有一哥们写得不错,故和大家一起分享. [分享]Java 线程池的原理与实现 这几天主要是狂看源程序,在弥补了一些以前知 ...
- Java线程池实现原理及其在美团业务中的实践
本文转载自Java线程池实现原理及其在美团业务中的实践 导语 随着计算机行业的飞速发展,摩尔定律逐渐失效,多核CPU成为主流.使用多线程并行计算逐渐成为开发人员提升服务器性能的基本武器.J.U.C提供 ...
- 我眼中的java线程池实现原理
最近在看java线程池实现方面的源码,在此做个小结,因为网上关于线程池源码分析的博客挺多的,我也不打算重复造轮子啦,仅仅用纯语言描述的方式做做总结啦! 个人认为要想理解清楚java线程池实现原理,明白 ...
- Java 线程池(ThreadPoolExecutor)原理分析与使用
在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 使用线程池的好处 1.降低资源消耗 可以重复利用 ...
- Java 线程池(ThreadPoolExecutor)原理解析
在我们的开发中“池”的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 有关java线程技术文章还可以推荐阅读:<关于java多线程w ...
- Java线程池实现原理与技术(ThreadPoolExecutor、Executors)
本文将通过实现一个简易的线程池理解线程池的原理,以及介绍JDK中自带的线程池ThreadPoolExecutor和Executor框架. 1.无限制线程的缺陷 多线程的软件设计方法确实可以最大限度地发 ...
- 深入浅出JAVA线程池使用原理1
前言: Java中的线程池是并发框架中运用最多的,几乎所有需要异步或并发执行任务的程序都可以使用线程池,线程池主要有三个好处: 1.降低资源消耗:可以重复使用已经创建的线程降低线程创建和销毁带来的消耗 ...
- Java线程池(ThreadPoolExecutor)原理分析与使用
在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 使用线程池的好处 1.降低资源消耗 可以重复利用 ...
随机推荐
- Android设备直接运行java项目?还杀不死?
思路:拿到dex可执行文件,使用android执行 使用idea创建java类库,写相关逻辑代码 使用idea导出该类库jar包 使用android dx工具 将jar文件转换为dex可执行文件 dx ...
- Linux使用yum安装JDK
安装jdk8 检查系统是否自带open-jdk输入命令: #rpm -qa|grep java #rpm -qa|grep jdk 如果没有输入信息表示没有安装,如果安装可以输入命令:#rpm -qa ...
- 安装Pygame(Python3.6,windows)
1. 本机为python3.6的环境 2. 到pygame官网下载对应系统,对应python版本的pygame文件,下载地址:https://pypi.python.org/pypi/Pygame/1 ...
- 如何让 curl 命令通过代理访问
如何让 curl 命令通过代理访问 Linux.中国 - 开源中文社区 2018-01-18 8909 阅读 技术 我的系统管理员给我提供了如下代理信息: IP: 202.54.1.1 Port: 3 ...
- this()基础用法
this()表示调用构造方法,此种调用只能用在构造方法中,即构造方法中调用构造方法this(实参). 1.this().this(实参)必须方法构造方法的第一行 2.在有参数构造方法中调用无参数构造方 ...
- jsp页面的html代码显示不出来,提示Uncaught SyntaxError: Unexpected token <
jsp页面的html代码显示不出来,提示Uncaught SyntaxError: Unexpected token < <input type="hidden" na ...
- C# WPF 通过委托实现多窗口间的传值
在使用WPF开发的时候就不免会遇到需要两个窗口间进行传值操作,当然多窗口间传值的方法有很多种,本文介绍的是使用委托实现多窗口间的传值. 在上代码之前呢,先简单介绍一下什么是C#中的委托(如果只想了解如 ...
- Ubuntu学习之路
一. Ubuntu简介 Ubuntu(乌班图)是一个基于Debian的以桌面应用为主的Linux操作系统,据说其名称来自非洲南部祖鲁语或科萨语的“ubuntu”一词,意思是“人性”.“我的存在是因为大 ...
- SCUCTF2018web部分wp
[签到] Web部分的签到题,打开连接后F12审查元素 可以看到有被隐藏起来的JSFuck密码,解码运行后可得flag [计算器] 打开后界面如上图,要求简单来说就是回答20道数学题目,每道题最多3s ...
- Startls Back 引起的 win10升级之后的闪屏问题
win10 更新之后出现闪频问题. 有人说是和startls back 有关,需要卸载startls back, 但是进入安全模式下显示此 程序无法打开,无法卸载. 后来看到有人更新到startls ...