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

一、实现思路

                      (网络盗图)

二、实现代码

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 #46 (Div. 2)

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

  2. ES6之导入模块时的内存共享

    项目结构 主页面 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> &l ...

  3. 7.27-8.10 Problems

    这是之前记录在word里的问题,现在誊到博客里.温故知新.时常回顾问题. 7.27 Bootstrap validator remote 验证出错 用Bootstrap validator插件验证表单 ...

  4. cloud server ribbon 自定义策略配置

    虽然ribbon默认为我们提供了多钟负载均衡策略,但有时候我们仍然需要自定义符合自身业务逻辑的规则 使用配置文件的方式:我们只需要在配置文件中添加配置 serviceId.ribbon.NFLoadB ...

  5. mvc下添加 EntityFramework的引用

    首先   打开工具-扩展和更新-联机-Visual Studio库,找到NuGet Package Manager 检查是否 安装,如果没有安装 先安装插件 安装成功后,右键点击‘引用’,如下图 然后 ...

  6. 设a、b、c均是0到9之间的数字,abc、bcc是两个三位数,且有:abc+bcc=532。求满足条件的所有a、b、c的值。

    题目描述 设a.b.c均是0到9之间的数字,abc.bcc是两个三位数,且有:abc+bcc=532.求满足条件的所有a.b.c的值. 输入描述: 题目没有任何输入. 输出描述: 请输出所有满足题目条 ...

  7. sqlserver数据库的物理存储格式和逻辑存储格式

    物理存储结构: 数据库文件在磁盘上的存储形式: 主数据文件:*.mdf.用来存储数据库的启动信息.存储部分或全部的数据.整个的数据库只能有一个主数据文件 辅助数据文件:*.ndf.用于存储主数据文件未 ...

  8. 任意格式视频转MP4格式

    下载ffmpeg解压,提取ffmpeg.exe 在mmfpeg.exe目录下新建批处理,内容如下 @echo off title 正在转换,mp4转换完成自动关闭 ffmpeg -i %1 -y -q ...

  9. code first 添加外键时,与原有的数据冲突ALTER TABLE 语句与 FOREIGN KEY 约束"FK_XXXXX"冲突

    问题的原因是新增的外键字段没有默认值,造成的,有很多方法可以解决,我这里通过修改生成的迁移文件,设定为可空,或者设置默认值即可.具体看情况使用.

  10. UML 图C#

    继承关系(类1继承类2) 代码: class Class1:Class2 { } class Class2 { } 实现(实现接口) 代码: interface interface1 { void s ...