一个线程池实例代码的分析
来源:互联网 发布:淘宝卖电影资源犯法 编辑:程序博客网 时间:2024/06/10 00:50
这段代码来源于孙卫琴老师的《java网络编程精解》里,这里通过分析一下这本书里线程池的实现方式来了解线程池的工作原理。首先上代码~~ 这里为了测试方便就直接把测试代码写在了线程池的实现类中间了,就是那俩个static函数。
package compass.my.thread;import java.util.LinkedList;public class MyThreadPool extends ThreadGroup{ /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub int size = 6 ; int runTasks = 9 ; MyThreadPool pool = new MyThreadPool(size); for(int i = 0 ; i < runTasks ; i++) { pool.execute(createTask(i)); } pool.join(); pool.close(); } public static Runnable createTask( final int taskID) { return new Runnable() { public void run() { System.out.println("Task :" + taskID + " start"); try { Thread.sleep(1000); }catch(InterruptedException e) { System.out.println(e); } System.out.println("Task :" + taskID + " end"); } }; } private boolean isClosed = false ; private LinkedList<Runnable> taskQueue ; private static int threadPoolID = 0 ; private int threadID ; /** * * * 内部类,工作线程,负责从工作队列中取出任务并执行 * @author BaiyangTX * */ class WorkThread extends Thread { public WorkThread() { super(MyThreadPool.this ,"WorkThread-"+(threadID++)); } @Override public void run() { while(true) { Runnable task = null ; try { task = getTask();//取出任务 }catch(InterruptedException e) { e.printStackTrace(); } if(task == null ) {// 当取出任务为空时,结束当前工作线程 return ; } try {// 执行取得的任务 task.run(); }catch(Throwable t) { t.printStackTrace(); } } } } /** * 构造方法,构造一个线程池 * @param poolSize-线程池的大小,即工作线程的数目 */ public MyThreadPool(int poolSize) { super("ThreadPool-"+(threadPoolID++)); setDaemon(true); taskQueue = new LinkedList<Runnable>(); for(int i = 0 ; i < poolSize ; i++) { new WorkThread().start(); } } /** * 想工作队列中提交一个任务,并且唤醒一个等待任务的工作线程去执行改任务 * @param task-需要被提交的任务 */ public synchronized void execute(Runnable task) { if(isClosed) { throw new IllegalStateException(); } if(task != null ) { taskQueue.add(task); notify(); } } /** * 从工作队列中获取一个任务,如果没有任务可获取,则挂起调用该方法的线程,直到任务队列中有任务。 * @return-工作队列中的任务,如果线程池已经关闭,则返回null值 * @throws InterruptedException */ protected synchronized Runnable getTask() throws InterruptedException { while(taskQueue.size() == 0) { if(isClosed) return null ; wait(); } return taskQueue.removeFirst(); } /** * 关闭线程池,清空任务队列,并且中断所有正在执行的工作线程 */ public synchronized void close() { if(!isClosed) { isClosed = true ; taskQueue.clear(); interrupt(); } } public void join() { synchronized(this) { isClosed = true ; notifyAll(); } Thread[] threads = new Thread[activeCount()] ; int count = enumerate(threads); for(int i = 0 ; i < count ; i++) { try { threads[i].join(); }catch(InterruptedException e) { e.printStackTrace(); } } } }
可以看到,线程池类除了构造函数外对外有3个函数可供调用。它们分别是
execute(Runnable task) :该函数用于向线程池池中提交任务。
join() : 调用该方法将等待线程池中所有的任务执行完毕。
close() : 关闭线程池,未执行的任务讲不会被执行,当前正在执行的任务会执行完毕。
同时还提供了一个protected方法。该方法由线程池的内部工作类调用。之所以被定义为protected类型是为了可以在该类被继承时,可以重写该方法。
该线程池类还定义了一个内部工作类,实际上正是该工作类的对象在执行着用户提交的任务。
public MyThreadPool(int poolSize)可以看到构造方法有一个参数,size,这个参数决定了线程池对象将会持有有多少个工作线程,以后用户提交的所有的任务(task)都是由这些工作线程去执行,所以合理的安排工作线程的数目是必须的。当然这也是线程池的用处之一。
private LinkedList<Runnable> taskQueue ;线程池持有一个任务列表。用户通过execute方法提交计算任务,任务会被添加到任务列表中,等待着被工作线程获取并执行。
private boolean isClosed = false ;
可以注意到,线程池还持有一个boolean变量isClosed。当线程池被关闭时,这个变量会被设置会true。
工作线程的run方法是一个死循环,所有的工作线程都在线程池对象被实例化的时候创建并执行。工作线程通过线程池对象提供的protected方法getTask从任务队列中获取任务。如果任务队列中有任务,则取出任务,否则线程将被挂起,直到有任务被提交。如果线程池已经关闭,则返回null值。
获取任务后判断,不为空的话就执行任务。
protected synchronized Runnable getTask() throws InterruptedException { while(taskQueue.size() == 0) { if(isClosed) return null ; wait(); } return taskQueue.removeFirst(); }
分析getTask方法,首先判断任务队列是否为空,不为空的话返回一个任务,否则判断,当线程池已经关闭则返回一个null,否则调用wait方法,挂起调用这个方法的工作线程。注意到这个方法是同步的,所以当一个工作线程被挂起的时候,由于其他的线程无法获得当前对象的锁,所以必须等待被挂起的线程获得任务或得到null才可以调用这个方法。
这里的wait方法是线程池对象的方法,所以被挂起的工作线程讲被加入当前线程对象的等待队列中,这时候我们分析execute方法。
public synchronized void execute(Runnable task) { if(isClosed) { throw new IllegalStateException(); } if(task != null ) { taskQueue.add(task); notify(); } }当然用户调用了execute方法后,任务除了被放入任务队列中,还会调用当前线程对象的notify方法,这个方法将会使被加入当前线程池对象的wait队列中的第一个工作线程进入就绪状态并且尽快执行。
是不是和生产者消费者的例子很相似??
任务(task)就是产品,工作线程就是消费者,用户是生产者,工作线程获取产品,没有产品就等待,用户生产产品,生产产品时将通知消费者。
这就是线程池中工作调度的形式。
最后看一下join方法,首先设置isClosed为true。
synchronized(this) { isClosed = true ; notifyAll(); }这个操作位于同步区域内,然后获取所有的活动工作线程,依次调用join方法等待执行完毕。
当用户调用join方法后,等待所有的任务被执行完毕后,将释放所有的线程资源。关闭线程池。
public synchronized void close()
{ if(!isClosed) { isClosed = true ; taskQueue.clear(); interrupt(); } }close方法与join类似,不过它不等待所有任务执行完毕,直接把任务队列clear掉,然后打断所有的正在工作的工作线程,注意,这里调用的interrupt()方法是继承自ThreadGroup的方法,所有处于阻塞状态的线程会得到一个InterruptedException,但是活动中的线程不会得到异常,但是调用isInterrupt方法会返回true值。
- 一个线程池实例代码的分析
- 一个完整的线程池的实例
- 一个简单的JAVA线程池实例
- 一个C++开发App的代码实例分析
- 用Python实现一个简单的线程池模型效果代码分析讲解
- 同步线程的一个实例
- 记录一个简单线程池的代码
- 一个notify()的实例分析
- ANR的一个实例分析
- [转载]java 代码构建线程池的实例
- 线程池(代码简单分析)
- Ceph代码分析---线程池
- 一个创建多个线程的实例
- thread包 一个简单的线程实例
- php和数据库结合的一个简单的web实例 代码分析 (php初学者)
- [学习笔记]Java代码构建一个线程池的自己学习写的实例,用这个你会更好的理解文章内容
- 一个侧屏滑动操作的实例(仿遇见)之三:代码分析
- 一个典型代码的分析
- 区位码转换为双字节字符(含汉字)
- MFC菜单、子菜单、菜单项的控制
- 树型动态规划
- 直接插入排序
- 自制服务器的琐碎局部资料
- 一个线程池实例代码的分析
- VC讲义大纲
- 通过了解MySpace的六次重构经历,来认识分布式系统到底该如何创建.
- POJ 1318 Word Amalgamation(我的水题之路——乱序字母匹配)
- 蛇、黄鼠狼与老鼠
- 蛇 与 蟹
- 三种想其他线程地址空间注入自己带吗
- Servlet 中文乱码问题及解决方案剖析
- Apache+php+mysql的安装与配置 - 之一(Apache的配置参数篇)