线程创建和销毁会消耗很多的资源,当我们创建线程时,会发现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. REST easy with kbmMW #1

    kbmMW 5.0支持REST服务器的开发,并且非常简单,下面看看如何实作一个REST服务器. 首先我们制作一个服务器应用程序,增加一个简单的Form,并放置kbmMW组件. 在Delphi中单击Fi ...

  2. 并发编程J.U.C之AQS剖析

    一.j.u.c简介 在说主题AQS之前,我们有必要先来说一下J.U.C 顾名思义J.U.C就是java.util.concurrent,java并发工具包.由我们的并发大师老爷子Doug Lea亲自操 ...

  3. jvm出现OutOfMemoryError时处理方法/jvm原理和优化参考

    The heap stores all of the objects created by your java program.The heap's contents is monitored by ...

  4. 前端入门Js笔记

    T 001 ____________--信息页面展示 需求分析: 有一个页面,在页面上有很多文字信息,且格式不一. 技术分析: html: 文字标签: 字体标签: 标题标签: 其他标签: 排版标签: ...

  5. FlowNet2.0论文笔记

    原论文标题:FlowNet 2.0: Evolution of Optical Flow Estimation with Deep Networks 文章是对FlowNet的进一步改进,主要贡献为如下 ...

  6. F - One Occurrence CodeForces - 1000F (线段树+离线处理)

    You are given an array aa consisting of nn integers, and qq queries to it. ii-th query is denoted by ...

  7. zencart重置用户登录密码sql

    zencart重置用户ID为99的登录密码为aaaaaaa ;

  8. 源码安装 qemu-2.0.0 及其依赖 glib-2.12.12

    源码安装qemu-2.0.0 下载源代码并解压 http://wiki.qemu-project.org/download/qemu-2.0.0.tar.bz2 .tar.gz 编译及安装: cd q ...

  9. Hadoop-No.2之标准文件格式

    标准文件格式可以指文本格式,也可以指二进制文件类型.前者包括逗号分隔值(Comma-Separated Value,CSV和可扩展的标记语言文本(Extensible Markup Language. ...

  10. HDU 6053 - TrickGCD | 2017 Multi-University Training Contest 2

    /* HDU 6053 - TrickGCD [ 莫比乌斯函数,筛法分块 ] | 2017 Multi-University Training Contest 2 题意: 给出数列 A[N],问满足: ...