作者:李新春 email:xinchunli1982@163.com
搜索之前先要用lucene进行索引,这是lucene的工作原理。后台建立索引的工作是一个线程程序运行。在jive中是一个定时任务。其整体的设计思路和jdk中的Timer 类的设计思路有点相似。
首先回忆一下Timer的使用。
有三个步骤:
1,定义一个类继承TimerTask
2, 实例化一个Timer类
3,利用Timer启动一个定时任务。
举例:
Public class SayHello extends TimerTask
{
Public void run(){}
}
Timer tasktimer = new Timer();
TimerTask timertask = new SayHello();
tasktimer. scheduleAtFixedRate(timertask,delay,period);
在jive中其实最终也是这样操作的,但是它在上面包装了几层,看起来好像有些晦涩,难以理解,下面我就层层剥开,透彻领悟一下大侠的设计思想。
最终的操作如下:
类TaskEngine:
public static TimerTask scheduleTask(Runnable task, long delay,long period)
{
TimerTask timerTask = new ScheduledTask(task);
taskTimer.scheduleAtFixedRate(timerTask, delay, period);
return timerTask;
}
不管返回值如何,其中调用也是用了三步,taskTimer是Timer类型的。在函数外面实例化:
taskTimer = new com.jivesoftware.util.Timer(true);TimerTask实例化,然后调用scheduleAtFixedRate 方法。但是为什么这么设计呢?按照自己的设计思路,应该做如下操作:
public class DbSearchManager implements Runnable{
public void run()
{
if (!searchEnabled) { //预留接口
return;
}
addTask(new IndexTask());
}
private void addTask (TimerTask task) {
Timer timer = new Timer();
timer.scheduleAtFixedRate(task, 1000, 1000);
}
class IndexTask extends TimerTask
{
public void run()
{
System.out.println("Indexing....");
}
}
}
线程类IndexTask负责建立索引,线程类DbSearchManager负责启动索引线程。
学习点: 如何在取消这个定时任务呢。当然在DbSearchManager中可以做到。
增加一个变量 private TimerTask task = null; 然后增加一个方法
public void cancel()
{
if(task == null)
return;
task.cancel();
}
}
addTask(new IndexTask());改为: task = new IndexTask() ; addTask(task); 欧了。
上面代码的缺点:这里只有一个IndexTask任务,代码量看起来不是很大,如果这里的任务有10个,100个,还要这么写吗?每个任务都要addTask(),然后顺序的执行么?有没有办法把这些任务执行管理起来?答案是肯定的。我只把这些任务生成出来,交给这个管理类去处理就ok了,至于怎么执行,是单线程,还是多线程执行是他的任务了。降低了代码的耦合度。
好了,有了上面的思想就前进了一大步,继续我们的设计。下面设计一个任务管理类,这个类必须能接纳和执行我们的任务。
public class TaskEngine {
//保存,接纳过来的任务
private static ArrayList<TimerTask> tasks = new ArrayList<TimerTask>();
private static Object lock = new Object();
private static Timer taskTimer ;
static
{
taskTimer = new Timer();
TaskEngineWorker worker = new TaskEngineWorker();
Thread thread = new Thread(worker);
thread.start();
}
public static void addTask(TimerTask task)
{
tasks.add(task);
}
public static void deleteTask(TimerTask task)
{
if(tasks.contains(task))
{
tasks.remove(task);
}
}
public static void scheduleTask(TimerTask task, long delay, long period)
{
taskTimer.scheduleAtFixedRate(task, delay, period);
}
public static TimerTask nextTask()
{
synchronized(lock) {
// Block until we have another object in the queue to execute.
while (tasks.isEmpty()) {
try {
lock.wait();
}
catch (InterruptedException ie) { }
}
return tasks.remove(tasks.size()-1);
}
}
private static class TaskEngineWorker implements Runnable {
public void run() {
while (true) {
nextTask().run();
}
}
}
}
上面的程序就和jive中的有点像了,但是有以下几点不同:
1, 这里我们开辟了一个TaskEngineWorker线程,jive中开辟了7个线程来执行任务。效率明显要高。
2, 我们用ArrayList来保存接收到的任务,jive中用LinkedList来接收
3, 我们加入的任务都是TimerTask的,不具有普遍性,对于一般的线程任务也要接收,jive中用一个类ScheduledTask进行转化。
根据以上几点,我们再次改进相关类。
最后,有点不同的是DbSearchManager类的构造函数中
timerTask = TaskEngine.scheduleTask(
this,autoIndexInterval*JiveGlobals.MINUTE,
autoIndexInterval*JiveGlobals.MINUTE);
把自己this 添加到任务中去。呵呵,知道为什么作者这么设计吗?刚才上面提到了,TaskEngine是一个线程执行的模式,既然DbSearchManager也是一个线程,那么他的定时执行任务,我们也把他加到TaskEngine中去吧,如何执行交给TaskEngine来处理,模式的功能真的很强大。用这些模式来改变自己的设计理念,如果DbSearchManager自己处理,就增加了代码的维护量,提高了代码的耦合度。
好了,到这里就完整的讲解了定时索引的所有结构和代码,从中可以体会作者的代码精妙之处。至于LinkedList 与 ArrayList 的性能比较,另篇处理。