二 Java利用等待/通知机制实现一个线程池
接着上一篇博客的 一Java线程的等待/通知模型 ,没有看过的建议先看一下。下面我们用等待通知机制来实现一个线程池.
本文的代码放到了github上,地址如下: git@github.com:jiulu313/ThreadPool.git
线程的任务就以打印一行文本来模拟耗时的任务。主要代码如下:
1 定义一个任务的接口。
/*
* 任务的接口
*/
public interface Task {
void doSomething();
}
2 实现一个具体的任务。
/*
* 具体的任务
*/
public class PrintTask implements Task{ //打印一句话,睡一秒,来模拟耗时的任务
@Override
public void doSomething() {
System.out.println("任务:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3 实现工作线程
/*
* 工作者线程
*/
public class Worker implements Runnable {
//线程是否正在运行
private boolean running = true; //保存Thread,方便start()
private Thread thread; //保存线程池的任务队列,作同步用
private LinkedList<Task> tasks; public void setThread(Thread thread) {
this.thread = thread;
} public void setTasks(LinkedList<Task> tasks) {
this.tasks = tasks;
} //启动此工作线程
public void start() {
if (thread != null) {
thread.start();
}
} // 关闭此工作线程
public void shutDown() {
running = false;
thread.interrupt();
} @Override
public void run() {
while (running) {
Task task = null; //对共享变量加锁,此处为任务队列,因为会有多个线程访问
synchronized (tasks) { //当条件不满足时,线程等待,见上一篇博文
while (tasks.isEmpty()) {
try {
//线程进入等待状态,并且释放锁
tasks.wait();
} catch (InterruptedException e) {
//感知到外部对此线程的中断操作
Thread.currentThread().interrupt();
return;
}
} //条件满足
task = tasks.removeFirst();
} //执行任务
if (task != null) {
task.doSomething();
}
}
}
}
4 创建一个线程池
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List; public class DefaultThreadPool implements ThreadPool {
private int maxWorksNum = 10;
private int minWorksNum = 1;
private int defaultWorksNum = 5; // 任务列表
private LinkedList<Task> tasks = new LinkedList<>(); // 工作线程列表
private LinkedList<Worker> workers = new LinkedList<>(); //工作线程个数
private int workerNum = defaultWorksNum; @Override
public void excute(Task task) {
// 添加一个工作,然后进行通知
if (task != null) {
synchronized (tasks) {
//添加到最后一个位置
tasks.addLast(task);
//通知等待的线程,有新的任务了
tasks.notify();
}
}
} // 关闭线程池
@Override
public void shutDown() {
for (Worker worker : workers) {
worker.shutDown();
}
} // 初始化工作者线程
public void initWorkers(int num) {
if (num > maxWorksNum) {
num = maxWorksNum;
} else if (num < minWorksNum) {
num = minWorksNum;
} else {
num = defaultWorksNum;
} for (int i = 0; i < workerNum; i++) {
//创建工作线程
Worker worker = new Worker(); //添加到工作队列
workers.add(worker); //新建一个线程对象,并将worker赋值
Thread thread = new Thread(worker); //设置线程对象,作启动,中断用
worker.setThread(thread); //设置任务队列,作同步用
worker.setTasks(tasks);
}
} // 启动线程池
public void start(){
if(workers != null){
for(Worker worker : workers){
//启动一个工作线程
worker.start();
}
}
} // 新增加工作线程,但是不能大于线程池最大线程数
@Override
public void addWorkers(int num) {
if (num <= 0) {
return;
} int remain = maxWorksNum - workerNum;
if (num > remain) {
num = remain;
} for (int i = 0; i < num; i++) {
Worker worker = new Worker();
workers.add(worker);
Thread thread = new Thread(worker);
thread.start();
} workerNum = workers.size();
} // 减少工作线程,至少留1个,不能减少到0
@Override
public void removeWorkers(int num) {
if(num >= workerNum || num <= 0){
return;
} for(int i =0;i<num;i++){
Worker worker = workers.getLast();
worker.shutDown();
} workerNum = workers.size();
} @Override
public int getTaskSize() {
return tasks.size();
} }
5 新建测试类
public class ThreadPoolTest {
public static void main(String[] args) throws InterruptedException {
//1 新建一个线程池
DefaultThreadPool pool = new DefaultThreadPool(); //2 设置线程池的大小为5
pool.initWorkers(5); //3 启动线程池
pool.start(); //4 往任务队列里面添加任务
for(int i = 0;i<100;i++){
pool.excute(new PrintTask());
} }
}
在eclipse中运行,结果部分截图如下:
好了,一个线程池就这样,这只是一个小例子,提示线程池的原理
其实实现工作中,一个线程池要考虑的问题远比这个多,也更复杂。
其中比较重要的两个方面:
1 线程池开几个线程为最合适?
我们知道,线程不是开的越多越好,而要根据业务的场景,硬件的指标,带宽的大小等等
一般线程池的个数为CPU核心数的个数加1 ,google的建议。此外可能还要具体分析业务
大,中,小的业务需求,也是不一样的。
大任务:比如下载一部电影,可能要十几分钟甚至几十分钟的任务
中任务:比如下载一幅图片,有1M以上了到十几M的大小的。
小任务:比如下载的是游戏的ico,就十几K的到1M以下的。
小任务可以多开几个线程。
中任务的可以保守点。
大任务的尽量不要开的线程太多
具体值还需要看具体业务,具体场景。这些只是建议。
2 线程用哪种队列,也是和上面有关系。
今天就到这了,后续还会抽时间研究线程并发这块,希望对大家有帮忙。
二 Java利用等待/通知机制实现一个线程池的更多相关文章
- 【Java并发基础】使用“等待—通知”机制优化死锁中占用且等待解决方案
前言 在前篇介绍死锁的文章中,我们破坏等待占用且等待条件时,用了一个死循环来获取两个账本对象. // 一次性申请转出账户和转入账户,直到成功 while(!actr.apply(this, targe ...
- Java Concurrency - wait & notify, 等待通知机制
生产者消费者问题是一个常见的多线程同步案例:一组生产者线程和一组消费者线程共享一个初始状态为空.大小为 N 的缓冲区.只有当缓冲区没满的时候,生产者才能把消息放入缓冲区,否则必须等待:只有缓冲区不空的 ...
- 内置锁(二)synchronized下的等待通知机制
一.等待/通知机制的简介 线程之间的协作: 为了完成某个任务,线程之间需要进行协作,采取的方式:中断.互斥,以及互斥上面的线程的挂起.唤醒:如:生成者--消费者模式.或者某个动作完成,可以唤醒下一 ...
- Java多线程之三volatile与等待通知机制示例
原子性,可见性与有序性 在多线程中,线程同步的时候一般需要考虑原子性,可见性与有序性 原子性 原子性定义:一个操作或者多个操作在执行过程中要么全部执行完成,要么全部都不执行,不存在执行一部分的情况. ...
- java多线程系列(三)---等待通知机制
等待通知机制 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我的理解 ...
- Java并发编程,Condition的await和signal等待通知机制
Condition简介 Object类是Java中所有类的父类, 在线程间实现通信的往往会应用到Object的几个方法: wait(),wait(long timeout),wait(long tim ...
- Java并发编程(04):线程间通信,等待/通知机制
本文源码:GitHub·点这里 || GitEE·点这里 一.概念简介 1.线程通信 在操作系统中,线程是个独立的个体,但是在线程执行过程中,如果处理同一个业务逻辑,可能会产生资源争抢,导致并发问题, ...
- JMM之Java线程间通讯——等待通知机制及其经典范式
在并发编程中,实际处理涉及两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体). 通信是指线程之间以何种机制来交换信息.在共享内存的并发模型里,线程之间共享程序的公共状 ...
- Java并发之等待/通知机制
目录 1 前言 1.1 先来段代码放松一下 2 Object wait()/notify() 2.1 一段入门代码 2.2 问题三连击 a.为什么官方说wait() 要放在while里面? b.为什么 ...
随机推荐
- window SVN设置忽略文件列表
进入checkout的项目文件夹. 执行 mvn install.生成 target文件夹. 如果这时候不想让target文件夹纳入版本控制.则进入子文件夹,在target文件夹上 右键执行 查看设置 ...
- GPON和820.1p学习及资料(zt)
1)hw的两个PPT不错,GPON技术基础.ppt和10G-GPON技术基础.ppt, 介绍了GPON的知识背景,标准的名称,帧协议. 尤其是详细对比了10G-PON和G-PON的区别,以及演进的道路 ...
- modelsim(3) - summary ,issue,tips
1) the OEM of modelsim is 10 times slower than offical questa 2)how to the file full path in the mod ...
- UML大战需求分析阅读笔记3
UML各种图的中文译名,因为翻译的原因可能会有所不一样,如:Sequence Diagram和Timing Diagram有时候都会被译成"时序图",这是最让人困扰的地方!Sequ ...
- mysql基础用法
<1>内置函数: locate('aa', '字段名');查询aa在字段中是否存在,返回1或0 replace('字段名','需替换的字符','替换后的字符') <2>函数: ...
- Unable to require openssl, install OpenSSL and rebuild ruby (preferred) or use non-HTTPS sources解决
解决方法 ruby -v rvm requirements brew install libyaml rvm pkg install openssl rvm install 2.3.1 --with- ...
- selenium—八种定位方法
find_element_by_id() find_element_by_name() find_element_by_class_name() find_element_by_tag_name() ...
- 【BZOJ2874】训练士兵(主席树)
题意:有一个N*M的矩阵,给出一些形如(x1,y1,x2,y2,s)的操作,代表(x1,y1)到(x2,y2)都被加上了s这个数 现在有一些强制在线的询问,询问(x1,y1)到(x2,y2)的和 对于 ...
- 解决: maven编译项目报“非法字符: \65279 ”错误
打包maven项目的时候,出现异常: [INFO] ------------------------------------------------------------------------ [ ...
- ajax省市区三级联动
jdbc+servlet+ajax开发省市区三级联动 技术点:jdbc操作数据库,ajax提交,字符拦截器,三级联动 特点:局部刷新达到省市区三级联动,举一反三可以做商品分类等 宗旨:从实战中学习 博 ...