wait/notify模拟线程池
线程创建和销毁会消耗很多的资源,当我们创建线程时,会发现cpu利用率很高,为了节省资源的使用,使用线程池是一个比较好的选择,当有任务需要执行时,随机分配给一条线程去执行,也可以删除任务,获取任务数量等。下面使用springboot构建一个简单的线程池。
自定义线程池
package com.demo.bootdemo.threadpool; import java.util.LinkedList;
import java.util.List; public class MyThreadPool {
// 线程用于执行任务,所以要具有执行任务,添加线程,减少线程,获取任务数量,释放线程,定时执行任务等操作
private LinkedList<Thread> threadPool = new LinkedList<Thread>();
// 存放需要执行的任务
private LinkedList<Job> jobs = new LinkedList<Job>();
// 线程池容量
private int capacity; public MyThreadPool() {
capacity = 10;
init();
} public MyThreadPool(int capacity) {
this.capacity = capacity;
init();
} /**
* 初始化
*/
private void init() {
for (int i = 0; i < capacity; i++) {
Thread th = new Thread(new MyRunnable(), "th_" + i);
threadPool.add(th);
th.start();
}
} /**
* 执行单个任务
*
* @param job
*/
public void executeJob(Job job) {
addJob(job);
} /**
* 批量执行任务
*
* @param jobList
*/
public void executeJobs(List<Job> jobList) {
addJobs(jobList);
} public void deleteJob(String jobKey) {
synchronized (jobs) {
Job delJob = null;
for (Job j : jobs) {
if (jobKey.equals(j.getJobKey())) {
delJob = j;
break;
}
}
// 删除
jobs.remove(delJob);
}
} private void addJobs(List<Job> jobList) {
synchronized (jobs) {
if (jobList != null && jobList.size() > 0) {
jobs.addAll(jobList);
jobs.notifyAll();
}
}
} private void addJob(Job job) {
synchronized (jobs) {
jobs.add(job);
jobs.notify();
}
} /**
* 获取任务数量
*
* @return
*/
public int getJobSize() {
return jobs.size();
} private class MyRunnable implements Runnable {
public void run() {
// 任务列表中没有任务,则等待,否则取出任务执行
while (true) {
Job job = null;
synchronized (jobs) {
if (jobs.isEmpty()) {
try {
jobs.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
job = jobs.removeFirst();
}
}
if (job != null) {
job.execute();
}
}
}
}
}
Job接口
package com.demo.bootdemo.threadpool; public abstract class Job { String jobKey; public String getJobKey() {
return jobKey;
} public void setJobKey(String jobKey) {
this.jobKey = jobKey;
}
public abstract void execute();
}
Job实现类,这里模仿了三种不同类型的Job, PrintJob,SayHelloJob和WriteFileJob
PrintJob
package com.demo.bootdemo.threadpool.myjob; import java.util.LinkedList; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import com.demo.bootdemo.threadpool.Job; public class PrintJob extends Job {
private Logger logger = LoggerFactory.getLogger(PrintJob.class);
private LinkedList<String> ls = new LinkedList<String>(); public PrintJob(String jobKey, LinkedList<String> ls) {
this.setJobKey(jobKey);
this.ls = ls;
} @Override
public void execute() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info(ls.toString() +", " + getJobKey());
} }
SayHelloJob
package com.demo.bootdemo.threadpool.myjob; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import com.demo.bootdemo.threadpool.Job; public class SayHelloJob extends Job {
private Logger logger = LoggerFactory.getLogger(SayHelloJob.class); public SayHelloJob(String jobKey) {
this.setJobKey(jobKey);
} @Override
public void execute() {
logger.info("Just say hello. " + getJobKey());
} }
WriteFileJob
package com.demo.bootdemo.threadpool.myjob; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import com.demo.bootdemo.threadpool.Job; public class WriteFileJob extends Job { private Logger logger = LoggerFactory.getLogger(WriteFileJob.class); public WriteFileJob(String jobKey) {
this.setJobKey(jobKey);
} @Override
public void execute() {
String fileName = "./" + System.currentTimeMillis();
File f = new File(fileName);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(f);
fos.write(String.valueOf(System.currentTimeMillis()).getBytes());
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
logger.info(String.format("write file. fileName: %s", fileName) + ", " + getJobKey());
}
} }
配置类
package com.demo.bootdemo.threadpool.properties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; @Configuration
@ConfigurationProperties(prefix = "pool.thread")
public class ThreadPoolProperties { private int count; public int getCount() {
return count;
} public void setCount(int count) {
this.count = count;
} }
配置文件applicaiton.properties
pool.thread.count=5
测试类入口
package com.demo.bootdemo.threadpool.listeners; import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List; import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent; import com.demo.bootdemo.threadpool.Job;
import com.demo.bootdemo.threadpool.MyThreadPool;
import com.demo.bootdemo.threadpool.myjob.PrintJob;
import com.demo.bootdemo.threadpool.myjob.SayHelloJob;
import com.demo.bootdemo.threadpool.myjob.WriteFileJob;
import com.demo.bootdemo.threadpool.properties.ThreadPoolProperties; public class ThreadPoolListeners implements ApplicationListener<ContextRefreshedEvent> { @Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ThreadPoolProperties bean = event.getApplicationContext().getBean(ThreadPoolProperties.class);
// 初始化线程池
MyThreadPool pool = new MyThreadPool(bean.getCount());
// 新建PrintJob任务
LinkedList<String> ls = new LinkedList<String>();
ls.add("a");
ls.add("b");
PrintJob printJob = new PrintJob("PrintJobKey000", ls); // 新建sayhellojob
SayHelloJob sayHelloJob = new SayHelloJob("SayHelloJobKey000"); // 新建writeFileJob
WriteFileJob writeFileJob = new WriteFileJob("WriteFileJobKey000"); List<Job> jobList = new ArrayList<>();
jobList.add(printJob);
jobList.add(sayHelloJob);
jobList.add(writeFileJob); // 执行以上三个任务
pool.executeJobs(jobList); jobList.clear();
for (int i = 0; i < 10; i++) {
sayHelloJob = new SayHelloJob("sayhellojobkey" + i);
jobList.add(sayHelloJob);
}
pool.executeJobs(jobList); // 删除任务
pool.deleteJob("sayhellojobkey7");
pool.deleteJob("sayhellojobkey8"); // 单独执行一个任务
writeFileJob = new WriteFileJob("writeJobkey_alone");
pool.executeJob(writeFileJob); } }
springboot启动类
package com.demo.bootdemo; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import com.demo.bootdemo.threadpool.listeners.ThreadPoolListeners; @SpringBootApplication
public class MythreadpoolApplication { public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(MythreadpoolApplication.class);
springApplication.addListeners(new ThreadPoolListeners());
springApplication.run(args); } }
输出结果
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.1.RELEASE) 20:39:18.382 [main] Starting MythreadpoolApplication on admin-PC with PID 27632 (D:\Programs\eclipseworkplace\springboot\mythreadpool\target\classes started by admin in D:\Programs\eclipseworkplace\springboot\mythreadpool)
20:39:18.385 [main] No active profile set, falling back to default profiles: default
20:39:18.855 [th_3] Just say hello. SayHelloJobKey000
20:39:18.855 [th_3] Just say hello. sayhellojobkey1
20:39:18.855 [th_3] Just say hello. sayhellojobkey2
20:39:18.855 [th_0] Just say hello. sayhellojobkey0
20:39:18.855 [th_3] Just say hello. sayhellojobkey3
20:39:18.855 [th_0] Just say hello. sayhellojobkey5
20:39:18.855 [th_0] Just say hello. sayhellojobkey6
20:39:18.855 [th_0] Just say hello. sayhellojobkey9
20:39:18.855 [th_1] Just say hello. sayhellojobkey4
20:39:18.858 [main] Started MythreadpoolApplication in 0.722 seconds (JVM running for 1.432)
20:39:19.854 [th_4] [a, b], PrintJobKey000
20:39:19.857 [th_2] write file. fileName: ./1558355958854, WriteFileJobKey000
20:39:19.858 [th_0] write file. fileName: ./1558355958855, writeJobkey_alone
从述打印日志看,sayhellojobkey7和sayhellojobkey8对应jobkey未打印出来,删除任务生效,所有任务都正常执行,两个WriteFileJob分别生成名称为1558355958854和1558355958855的文件,查看当前路径,也确实可以找到这样的文件。
将本案例打包,放入linux执行,使用jstack查看所有线程状态
这些线程基本上都处于WAITING状态,不具有锁,待Job任务被添加到集合中时,将唤醒这些线程,处理Job
wait/notify模拟线程池的更多相关文章
- 使用ThreadGroup模拟线程池
参考文章: [1]创建线程池 http://sunnylocus.iteye.com/blog/223327?page=2#comments [2]线程组ThreadGroup http://hub ...
- wait/notify模拟连接池
连接池中的连接可重复使用,减少每次新建和烧毁连接对资源的消耗,但连接池的容量大小也要设置合理,否则也会占用多余的资源.连接池的基本功能是获取连接和释放连接 连接在java中也是一个类,连接对象是一个普 ...
- 二 Java利用等待/通知机制实现一个线程池
接着上一篇博客的 一Java线程的等待/通知模型 ,没有看过的建议先看一下.下面我们用等待通知机制来实现一个线程池 线程的任务就以打印一行文本来模拟耗时的任务.主要代码如下: 1 定义一个任务的接口 ...
- java中线程池的几种实现方式
1.线程池简介: 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力. 假设一个服务器完成一项任务所需时间为:T1 创建 ...
- [.NET] 自己实现任务池(模仿线程池)
线程池虽然好用,但限制也不少: (1)总觉得默认的 MaxThread 小了一点,每次使用都要手工调大= = (2)任务不能等待完成 (3)任务一旦加入不能取消,甚至不知道是正在排队/正在执行/执行完 ...
- Java线程池的实现
线程池的作用: 一个线程的周期分为:创建.运行.销毁三个阶段. 处理一个任务时,首先创建一个任务线程,然后执行任务,完了还要销毁线程.而线程只有处于运行状态的时候,才是真的在处理我们交给它的任务,这个 ...
- 线程池的原理及实现 (zhuan)
http://blog.csdn.net/hsuxu/article/details/8985931 ************************************************* ...
- Android(java)学习笔记267:Android线程池形态
1. 线程池简介 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力. 假设一个服务器完成一项任务所需时间为:T1 创建线程时间, ...
- java多线程总结五:线程池的原理及实现
1.线程池简介: 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力. 假设一个服务器完成一项任务所需时间为:T1 创 ...
随机推荐
- Python实现串口通信(pyserial)
pyserial模块封装了对串口的访问,兼容各种平台. 安装 pip insatll pyserial 初始化 简单初始化示例 import serial ser = serial.Serial('c ...
- kbmMW均衡负载与容灾(1)
kbmMW为均衡负载与容灾提供了很好的机制,支持多种实现方式,现在看看最简单的一种,客户端控制的容灾和简单的负载均衡. 现在,我们将kbmMWServer部署到不同的服务器,或者在同一服务器部署多份实 ...
- Invalid property value
又见这个错误!头几天同事遇到这个问题,我查到去年写的并按此解决了,原文在这里,查了半天,才查出是ftShortInt造成的这个错误. 当我们在设计期将ClientQuery.Active设置为True ...
- vue中移动端滚动事件,点击一次触发了事件两次(better-scroll)
解决办法一: 将button标签换成a标签 问题代码: <span class="submitBtn" @click.stop="replyReport()&quo ...
- linux学习笔记七
#文件权限很重要,有些时候删除和新建文件没有权限根本操作不了,linux一切皆是文件,所以必须得了解下权限了. 文件的一般权限 简单的ls -ld 命令就能看到权限,dr-xr-x---补全应该是dr ...
- JS批量绑定事件
,,,,] for(var j in a){ $("#" + j).click(function () { // 前提是先动态生成id是j的标签 var id_cm = $(thi ...
- 2.2.EJB_Bean
1.EJB中的三种Bean 1.会话bean(sessionbean) 负责与客户端交互.是编写业务逻辑的地方.在会话Bean中可以通过jdbc直接操作数据厍.但大多数情况下都是通过实体bean来完 ...
- Linux使用storcli工具查看服务器硬盘和raid组信息
1.简介 MegaCli 是LSI公司官方提供的SCSI卡管理工具,由于LSI被收购变成了现在的Broadcom,所以现在想下载MegaCli, 需要去Broadcom官网查找Legacy产品支持,搜 ...
- idea 下gradle创建springboot 项目
InterlijIdea 开发环境下创建基于springBoot的项目. 环境 1.jdk1.5以上 2.interlijidea 15 以上 步骤 1.File –>new –>Proj ...
- zencart1.5.x版管理员密码90天到期后台进入不了的解决办法
zencart1.5.x版管理员密码90天到期后如果不想更改密码,可以直接在数据库运行以下sql语句. 将pwd_last_change_date(密码最后变换日期)2014-11-11 11:11: ...