线程创建和销毁会消耗很多的资源,当我们创建线程时,会发现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模拟线程池的更多相关文章

  1. 使用ThreadGroup模拟线程池

    参考文章: [1]创建线程池 http://sunnylocus.iteye.com/blog/223327?page=2#comments [2]线程组ThreadGroup  http://hub ...

  2. wait/notify模拟连接池

    连接池中的连接可重复使用,减少每次新建和烧毁连接对资源的消耗,但连接池的容量大小也要设置合理,否则也会占用多余的资源.连接池的基本功能是获取连接和释放连接 连接在java中也是一个类,连接对象是一个普 ...

  3. 二 Java利用等待/通知机制实现一个线程池

    接着上一篇博客的 一Java线程的等待/通知模型 ,没有看过的建议先看一下.下面我们用等待通知机制来实现一个线程池 线程的任务就以打印一行文本来模拟耗时的任务.主要代码如下: 1  定义一个任务的接口 ...

  4. java中线程池的几种实现方式

    1.线程池简介:    多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.        假设一个服务器完成一项任务所需时间为:T1 创建 ...

  5. [.NET] 自己实现任务池(模仿线程池)

    线程池虽然好用,但限制也不少: (1)总觉得默认的 MaxThread 小了一点,每次使用都要手工调大= = (2)任务不能等待完成 (3)任务一旦加入不能取消,甚至不知道是正在排队/正在执行/执行完 ...

  6. Java线程池的实现

    线程池的作用: 一个线程的周期分为:创建.运行.销毁三个阶段. 处理一个任务时,首先创建一个任务线程,然后执行任务,完了还要销毁线程.而线程只有处于运行状态的时候,才是真的在处理我们交给它的任务,这个 ...

  7. 线程池的原理及实现 (zhuan)

    http://blog.csdn.net/hsuxu/article/details/8985931 ************************************************* ...

  8. Android(java)学习笔记267:Android线程池形态

    1. 线程池简介  多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.     假设一个服务器完成一项任务所需时间为:T1 创建线程时间, ...

  9. java多线程总结五:线程池的原理及实现

    1.线程池简介:     多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.        假设一个服务器完成一项任务所需时间为:T1 创 ...

随机推荐

  1. Java高并发程序设计学习笔记(七):并行设计模式

    转自:https://blog.csdn.net/dataiyangu/article/details/87123586 什么是设计模式架构模式设计模式代码模式(成例 Idiom)单例模式普通单例假如 ...

  2. 解决IDEA报错Could not autowire. There is more than one bean of 'xxx' type

    更新项目之后IDEA突然出现了这样的报错信息.显示Could not autowire. There is more than one bean of 'xxx' type.这个错误的意思是xxx类型 ...

  3. current status of the installation and the internationalization of Samba 3.0

    Only about 8 months from release of Samba 3.0.0, there is beginning to be the transition from 2.2.x. ...

  4. kubernetes管理机密信息

    一.启动应用安全信息的保护: Secret介绍: 应用启动过程中可能需要一些敏感信息,比如访问数据库的用户名密码或者秘钥.将这些信息直接保存在容器镜像中显然不妥,Kubernetes 提供的解决方案是 ...

  5. 自然语言处理(三) 预训练模型:XLNet 和他的先辈们

    预训练模型 在CV中,预训练模型如ImagNet取得很大的成功,而在NLP中之前一直没有一个可以承担此角色的模型,目前,预训练模型如雨后春笋,是当今NLP领域最热的研究领域之一. 预训练模型属于迁移学 ...

  6. java8学习之深入函数式接口与方法引用

    函数式接口: 函数式接口[FunctionalInterface]是整个Lambda表达式的一个根源,换句话来说java8中的Lambda表达式要想彻底掌握,前提是要彻底理解好函数式接口,所以这次继续 ...

  7. 51Nod 1534 棋盘阻挡博弈

    很简单的可以知道 如果P在V的右上角 必输 如果P在V的左下角 必赢 接下里还剩下左上角和右下角两种情况 两种情况其实相同 P是挡不住V通过对角线方向向下/左的移动的 即两者不会相互影响 所以我们只要 ...

  8. thinkphp方法success和error跳转时间以及返回ajax

    Action类的success和error方法第三个参数为数字时候,表示指定页面跳转时间,例如: $,); $,); 如果是ajax跳转 必须用true: $this->success('操作成 ...

  9. libusb_bulk_transfer 说明

    libusb_bulk_transfer函数说明   API_EXPORTED int libusb_bulk_transfer(struct libusb_device_handle *dev_ha ...

  10. 【构造 meet in middle 随机 矩阵树定理】#75. 【UR #6】智商锁

    没智商了 变式可见:[构造 思维题]7.12道路建设 当你自信满满地把你认为的正确密码输入后,时光机滴滴报警 —— 密码错误.你摊坐在了地上. 黑衣人满意地拍了拍你的肩膀:“小伙子,不错嘛.虽然没解开 ...