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

一、实现思路

                      (网络盗图)

二、实现代码

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. 专项测试——移动app安装包检测

    一.背景和现状 安装包的重要性无需多提,针对安装包质量控制越来越严格和规范,包括证书.文件大小.安装成功率等,APP的证书及混淆是影响APP的安装成功率及代码安全性的很大因素,随着功能迭代,安装包也会 ...

  2. unittest 单元测试

    unittest 单元测试: 1,单元测试是指对软件中最小可测试单元进行检查和验证.对于单元测试中单元的含义,一般来讲,要根据实际情况去判定其具体含义. 2,unitest=TestCase + Te ...

  3. Linux后台运行进程

    方式一: /bin/bash /var/www/html/crontab.sh & 缺点:终端关闭的话,进程也会关闭. 方式二: nohup /bin/bash /var/www/html/c ...

  4. undefined reference to...

    wj@wj-Inspiron-:~/Downloads/LBD_Descriptor/build$ cmake .. -- Configuring done -- Generating done -- ...

  5. YII2中操作数据库的方式

    一.以createCommand方式: // YII2中通过createCommand来处理数据库 // 查询多条记录 // {{%user}} 表示如果设置了表前缀,YII会自动帮你替换 $data ...

  6. Struts2框架之Action类的访问

    1. 通过<action>标签中的method属性,访问到Action中的具体的方法. * 传统的配置方式,配置更清晰更好理解!但是扩展需要修改配置文件等! * 具体的实例如下: * 页面 ...

  7. c# 24种设计模式5原型模式(Prototype)

    前言 原型模式其实C# Object中已经提供了一个Clone( )方法,平时很少用到,最近读Retrofit源码时候看到有这种使用方式. 定义 原型模式就是在系统clone()标记的基础上,对Clo ...

  8. iOS.XcodeUsage

    1. Customizing Xcode File Templates http://blog.highorderbit.com/2009/03/15/customizing-xcode-cocoa- ...

  9. android xml解析中的null问题

    当我们从服务器或者xml文件加载xml进行解析的时候,往往报告 nullpointer 错误.这是原始代码: String short_name = doc.getElementsByTagName( ...

  10. MySQL 系列(一)安装

    MySQL 系列(一)安装 以 Centos7 下安装 MySQL 5.6 为例. 一.环境准备 (1) 下载 下载地址: https://dev.mysql.com/get/Downloads/My ...