项目中有个自动归档的功能,自动给过期的个人单位等数据进行归档处理。
基本的思路是这样的:使用另外一个线程,专门处理定时归档这件事,但是看了一下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.newFixedThreadPool():
创建固定大小的线程池,每提交一次任务创建一个线程,直到大小达到,异常线程会有新的线程替代。
Executors.newCachedThreadPool():
创建一个可缓存的线程池。其中的线程空闲60秒则开始回收,对线程数量无限制
Executors.newScheduledThreadPool();
创建一个无线大小的线程池,可以定时,周期性的执行任务。
<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()方法,这个时候已经运行的任务好像是要结束了。不过这时候如果新任务加入,可能又有新的线程去干活了,有线程活着程序就不会关闭,我们都懂得。所以说在关闭的同时要防止其他任务再进来。