二 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.为什么 ...
随机推荐
- AJAX实现跨域的三种种方法(代理,JSONP,XHR2)
由于在工作中需要使用AJAX请求其他域名下的请求,但是会出现拒绝访问的情况,这是因为基于安全的考虑,AJAX只能访问本地的资源,而不能跨域访问. 比如说你的网站域名是aaa.com,想要通过AJAX请 ...
- vim命令的妙用
进来看到了一篇博文,讲述的是无插件Vim编程技巧,然后看到里面还有一些其余的关于vim的博文,感觉值得收藏:酷 壳 – CoolShell.cn 这里挑选出其中一篇,这也是自己已经学习过的: 无插件V ...
- Web API的Log问题
建立了一个Web API项目,在WebApiConfig.cs中已经启用了Tracing. config.EnableSystemDiagnosticsTracing(); 同时web.config中 ...
- Android之动画的学习(转载)
Android动画学习笔记-Android Animation 3.0以前,android支持两种动画模式,tween animation,frame animation,在android3.0中 ...
- adb无法使用,提示error: unknown host service的解决办法
此时,需要辨别电脑的5037端口被哪个应用程序占用的方法:(使用adb时需要5037端口是空闲的) 1. 打开命令行,输入命令:netstat -ano |findstr "5037&quo ...
- VC++ 截屏代码,并保存为想要的格式(BMP,JPG,PNG,GIF等格式)
void CCaptionScreenDlg::Screen(char* filename) { HDC hdcSrc = ::GetDC(NULL); int nBitPerPixel = GetD ...
- EMIF接口的寻址问题
2014年8月26日,一个网友提出了一个关于EMIF访问地址很典型的问题,在此我阐述一下我的个人理解. 一.提出问题 他问的问题没有这么简单,但是差不多就是这种意思,我在晚上找个典型事例,如下: DS ...
- 执行超过1个小时的SQL语句
SELECT MO.MO_ID, MO.ITEM, MO.QTYORDERED, MO.PLANNEDSTARTDATE, BR.MAXLOTSIZE FROM TEMP_MO MO, (SELECT ...
- funny_python 01 import antigravity
在Python里面import antigravity 会发现另一个小彩蛋 Tada~ 其实就是打开了xkcd的一个python主题漫画 鼠标停留在图片上,还会看到标题: "I wrote ...
- A. Writing Code 完全背包
http://codeforces.com/contest/543/problem/A 一开始这题用了多重背包做,结果有后效性. 就是如果6,这样拆分成 1 + 2 + 3的,那么能产生3的就有两种情 ...