项目收获-手动写个线程池。。

原创 2015年07月07日 22:22:06

项目中有个自动归档的功能,自动给过期的个人单位等数据进行归档处理。

基本的思路是这样的:使用另外一个线程,专门处理定时归档这件事,但是看了一下Timer实现,发现有好多缺点。

测试1:

<pre name="code" class="java">public static void main(String[] args) {
	Timer timer = new Timer();
	timer.schedule(new TimerTask() {
		public void run() {
			System.out.println(System.currentTimeMillis());
			System.out.println(Thread.currentThread());
			try {
				Thread.sleep(2000);
			}catch (InterruptedException e) {
				e.printStackTrace();
			}
		}, 0);
	timer.schedule(new TimerTask() {
		public void run() {
			System.out.println(System.currentTimeMillis());
			System.out.println(Thread.currentThread());
		}
		}, 1);
}



打印结果如下:
1436275510043
Thread[Timer-0,5,main]
1436275512044
Thread[Timer-0,5,main]

说明这个定时器功能基本就一个线程,然后里面的任务做完一个再去取一个。如果一个任务时间太长了会影响下一个任务的执行。

测试2:

public static void main(String[] args) {
		Timer timer = new Timer();
		timer.schedule(new TimerTask() {
			public void run() {
				System.out.println("任务1执行-");
				throw new RuntimeException("抛个异常");
			}
		}, 0);
		timer.schedule(new TimerTask() {
			public void run() {
				System.out.println("任务2执行-");
			}
		}, 1);
	}
结果如下:

任务1执行-
Exception in thread "Timer-0" java.lang.RuntimeException: 抛个异常
	at a.thread.timer.Test$1.run(Test.java:12)
	at java.util.TimerThread.mainLoop(Timer.java:555)
	at java.util.TimerThread.run(Timer.java:505)
说明这个一旦线程出现错误,后面的任务就全没了,不怎么可靠。


与线程池相比,线程池的自愈能力是非常不错的:

线程池的作用:


1.减少创建和销毁线程的次数,每个线程都可以重复利用,可执行多个任务。

2.集中管理线程,可以根据系统所能承受的能力来设置线程池中线程的数目,防止开了过多的线程而是服务器运行缓慢(每个线程需要大约1M内存)。

四种线程池的创建:
Executors.newSingleThreadExecutor():
创建一个单线程的线程池,异常线程会有新的线程替代。其他任务队列等待。
Executors.newFixedThreadPool():
创建固定大小的线程池,每提交一次任务创建一个线程,直到大小达到,异常线程会有新的线程替代。
Executors.newCachedThreadPool():
创建一个可缓存的线程池。其中的线程空闲60秒则开始回收,对线程数量无限制
Executors.newScheduledThreadPool();
创建一个无线大小的线程池,可以定时,周期性的执行任务。


类用起来非常方便,传几个参数,几步就行。


用这个没多少意思,源代码还不想去看,我就想自己写个池试试。。。。


好像写个池得要这么些类:

ThreadWork:实际干活的线程
ThreadPool:存放ThreadWork的池
ThreadPoolManager:对外提供操作的类
ThreadQueue:等待执行的队列

贴代码:
<pre name="code" class="java">public class WorkThread extends Thread {
	// 方便控制线程开关
	private boolean flag = true;

	@Override
	public synchronized void run() {
		while (flag) {
			while (ThreadQueue.getQueue().hasMoreTask()) {
				if (!flag) {
					return;
				}
				ThreadQueue.getQueue().nextTask().run();
				if (!flag){
					System.out.println(Thread.currentThread()+"任务执行完毕,停止运行------------不再睡眠");
					return;
				}
			}
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void close() {
		flag = false;
	}
}





package a.thread.threadpool;

public class ThreadPool {

	private int maxThreadCount = 120;
	private int defaultThreadCount = 100;
	private WorkThread[] threads;

	ThreadPool(int threadCount) {
		this.defaultThreadCount = threadCount;
	}

	ThreadPool() {
	}

	{
		threads = new WorkThread[maxThreadCount];
		for (int i = 0; i < defaultThreadCount; i++) {
			threads[i] = new WorkThread();
		}
	}

	static ThreadPool obtain() {
		return new ThreadPool();
	}

	/**
	 * 返回空闲的线程
	 * */
	WorkThread getFreeThread() {
		for (int i = 0; i < defaultThreadCount; i++) {
			Thread.State state = threads[i].getState();
			if (state.equals(Thread.State.NEW)
					|| state.equals(Thread.State.WAITING)) {
				return threads[i];
			}
		}
		if (defaultThreadCount < maxThreadCount) {
			System.out.println("线程数不够,准备创建新的,当前数量为->" + defaultThreadCount);
			threads[defaultThreadCount] = new WorkThread();
			return threads[defaultThreadCount++];
		}
		return null;
	}

	/**
	 * 关闭所有的线程
	 * */
	public void closeAll() {
		for (WorkThread thread : threads) {
			if (thread != null)
				thread.close();
		}
	}
}

package a.thread.threadpool;

/**
 * 线程池管理器
 * */
public class ThreadPoolManager {
	private ThreadQueue queue = ThreadQueue.getQueue();
	private ThreadPool threadPool = ThreadPool.obtain();
	private boolean work = true;

	/**
	 * 执行一个线程
	 * */
	public void execute(Runnable task) {
		if (task == null) {
			throw new RuntimeException();
		}
		if (!work) {
			System.out.println("线程池已准备关闭,无法加入新的任务");
			return;
		}

		queue.addTask(task);
		WorkThread workThread = threadPool.getFreeThread();
		if (workThread != null) {
			if (workThread.getState().equals(Thread.State.NEW)) {
				workThread.start();
			} else if (workThread.getState().equals(Thread.State.WAITING)) {
				synchronized (workThread) {
					workThread.notify();
				}
			}
		}
	}

	public void cancel() {
		work = false;
		threadPool.closeAll();
	}
}
package a.thread.threadpool;

import java.util.LinkedList;
import java.util.Queue;

/**
 * 线程队列
 * */
public class ThreadQueue {
	private static ThreadQueue threadQueue=new ThreadQueue();
	public static ThreadQueue getQueue() {
		return threadQueue;
	}

	private Queue<Runnable> tasks;
	{
		tasks = new LinkedList<Runnable>();
	}

	public synchronized boolean hasMoreTask() {
		return !tasks.isEmpty();
	}

	public synchronized Runnable nextTask() {
		return tasks.poll();
	}

	public void addTask(Runnable task) {
		tasks.offer(task);
	}
}



就是这样,我测试了几次,感觉还行。

让线程不关闭,然后等待任务进来执行任务,这个肯定是要用回调的。

怎么让线程不死掉,遇到任务的时候又能执行?

我想到了2个方法:

1.用while(true)一直绕回圈,检查队列中的任务,有就执行,没有就接着跑,这个就像是android中的Looper

2.用obj.wait(),等待任务来了,来一个唤醒他一下。

但是whilt(true)我一直看他不爽,因为一直跑没任务的任务,总感觉好浪费内存,wait的字面意思就舒服多了。。。。这个完全是个人观点

wait()用的是本地方法,没法看到源代码,网上也没找到whilt(true)与wait到底哪个更好的说法,,

其实我用了wait是因为Timer里面他用的是wait。。。

可是android的Looper他用的while(true)


后来想了想,应该是这样的(也是个人观点):

android的Looper负责处理界面的显示相关,要求实时性非常高,一个线程一直跑,而且界面操作本来就频繁,所有说这个用while(true)一直查找队列里的任务是很好的选择,而wait与notify线程间的通信可能需要一点时间,没while(true)来的直接。

而我们一般用的普通任务,就拿项目中的自动归档,可能是一天才执行一次,那24小时让他while(true)跑着也不是一个意思,然他睡着,等到任务时间到了再让他起来干活。


while(true)与wait问题到此为止。。


写的时候遇到的问题:

一开始写的时候,从队列中取任务,并且扔给工作线程任务是在ThreadPoolManger中执行的,后来测试的时候发现个问题:这个execute方法阻塞了,因为这里把队列中的任务取出来执行,如果里面的任务耗时很久,那调用execute方法的线程就阻塞住了。

当时的想法就是我再写一个线程,把这阻塞的这一部分代码放到另外一个线程里面。这个线程专门处理将队列中的任务拿到工作线程中执行的操作。但是这个有点麻烦,锁加来加去的。

直接把队列放到workThread中,让线程自己去处理,自己阻塞去,工作线程阻塞了就说明任务还没做完,任务做好了接着检查queue中有没有任务,有就接着做,这里记得给队列加个锁。这样就行了。


还有关闭线程池,其中也遇到了关闭了池后程序无法结束的问题,因为ThreadPoolManager中,调用了ThreadPool的closeAll()方法,这个时候已经运行的任务好像是要结束了。不过这时候如果新任务加入,可能又有新的线程去干活了,有线程活着程序就不会关闭,我们都懂得。所以说在关闭的同时要防止其他任务再进来。



什么时候手动创建线程而不使用线程池

1、需要自定义线程的优先级,线程池中线程总是Normal2、需要一个前台线程,线程池中线程是后台线程     非UI线程最好使用线程池创建为后台线程,常常关闭一个软件之后,仍然占有内存,就是由于创建了...

自己手动写个线程池

前言:   要自定义一个线程池,首先要有存固定的线程数目的阻塞队列,还要有个存放任务的阻塞队列。   如果任务放入时有线程为空闲直接交付于它执行否则就放入任务队列中   在内部有个线程会不断的扫...

什么时候手动创建线程而不使用线程池

转自:http://blog.csdn.net/love__coder/article/details/6414936 1、需要自定义线程的优先级,线程池中线程总是Normal 2、需要一...

线程池基础类_ThreadPoolExecutor (JDK1.8)

ThreadPoolExecutor简介  ThreadPoolExecutor使用线程池执行提交的任务,还维护一些基本统计信息,例如已完成任务的数量。   为了适应大多数场景,该方法提供了许多可调...

如何创建Java中的线程池

线程是Java的一大特性,它可以是给定的指令序列、给定的方法中定义的变量或者一些共享数据(类一级的变量)。在Java中每个线程有自己的堆栈和程序计数器(PC),其中堆栈是用来跟踪线程的上下文(上下文是...

线程池系列一:线程池作用及Executors方法讲解

文章来源:http://zy116494718.iteye.com/blog/1704344 线程池的作用:      线程池作用就是限制系统中执行线程的数量。      根据系统的...

线程池创建原理及实现

本文给出了一个通用的线程池框架,该框架将与线程执行相关的任务进行了高层次的抽象,使之与具体的执行任务无关。另外该线程池具有动态伸缩性,它能根据执行任务的轻重自动调整线程池中线程的数量。文章的最后,我们...

java源码如何创建线程

转载自:http://blog.csdn.net/jeffhtlee/article/details/12751825  Java的线程是如何创建的,是直接调用OS的API,还是有自己的“抽象线...

Java 线程池 四种创建方式

Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 new...

Java并发系列-5、如何创建并运行java线程

Java线程类也是一个object类,它的实例都继承自java.lang.Thread或其子类。 可以用如下方式用java中创建一个线程: 1 Tread thread = new ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:项目收获-手动写个线程池。。
举报原因:
原因补充:

(最多只允许输入30个字)