关闭

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

标签: 线程池thread线程
832人阅读 评论(0) 收藏 举报
分类:

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

基本的思路是这样的:使用另外一个线程,专门处理定时归档这件事,但是看了一下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()方法,这个时候已经运行的任务好像是要结束了。不过这时候如果新任务加入,可能又有新的线程去干活了,有线程活着程序就不会关闭,我们都懂得。所以说在关闭的同时要防止其他任务再进来。



0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:96736次
    • 积分:1911
    • 等级:
    • 排名:千里之外
    • 原创:91篇
    • 转载:11篇
    • 译文:0篇
    • 评论:15条
    博客专栏
    最新评论