- 浏览: 34112 次
- 性别:
- 来自: 合肥
博客专栏
-
我和Java数据库操作的那...
浏览量:9265
最新评论
-
ivanlw:
楼主用的这个snapito好神奇啊……是直接把图片链接设置成他 ...
ibookmark.me上线了! -
succinite:
使用TortoiseGit, 出现以下错误。fatal: ht ...
一个简单的JAVA后台程序框架 -
mazhiyuan:
引用很显然,之前所说的梦想,并非是真正心中所想,而只不过想“找 ...
也谈梦想 -
cevin15:
说到我心里去了。现在处于离职状态。也是对前途一片迷茫~
也谈梦想 -
lwjlaser:
lettoo 写道lwjlaser 写道这篇博客第一个示例代码 ...
我和JAVA数据库操作的那些事儿(3)
今天在看apache chainsaw这个项目的源代码时,无意中发现了一个非常简单的Job Scheduler的实现,源代码可以看这里:http://svn.apache.org/repos/asf/logging/chainsaw/trunk/src/main/java/org/apache/log4j/scheduler/ ,其中一个是Scheduler,另一个是Job接口。
Scheduler介绍道:
* A simple but still useful implementation of a Scheduler (in memory only).
* <p/>
* This implementation will work very well when the number of scheduled job is
* small, say less than 100 jobs. If a larger number of events need to be
* scheduled, than a better adapted data structure for the jobList can give
* improved performance.
*
* @author Ceki
*/
测试一下这个Scheduler,写一个非常简单的SimpleJob来实现Job接口。
package cn.lettoo.scheduler; import java.text.SimpleDateFormat; import java.util.Date; public class SimpleJob implements Job { private String name; public SimpleJob(String name) { this.name = name; } public void execute() { Date now = new Date(System.currentTimeMillis()); System.out.println(String.format("%s: %s executed by thread %s", SimpleDateFormat.getDateTimeInstance().format(now), this.name, Thread.currentThread().getName())); } }
再写一个测试类:
package cn.lettoo.scheduler; public class JobTest { public static void main(String[] args) { Scheduler scheduler = new Scheduler(); Job job1 = new SimpleJob("job1"); scheduler.schedule(job1, System.currentTimeMillis(), 5000); scheduler.start(); } }
*这里的scheduler.schedule(job1, System.currentTimeMillis(), 5000);表示立即运行,且每5秒运行一次。
执行结果如下:
2011-10-14 22:13:03: job1 executed by thread Thread-0
2011-10-14 22:13:08: job1 executed by thread Thread-0
......
这样一个简单的Job Scheduler就实现了,但我发现这样只是一个单线程的Job Scheduler,假如我每个Job运行时间是10秒,而间隔是5秒,同时有多个Job运行的话,这个Scheduler的效率还是很差的。
改动一下SimpleJob,让Job运行时sleep 10秒钟,来模拟job运行10秒。
public void execute() { Date now = new Date(System.currentTimeMillis()); System.out.println(String.format("%s: %s executed by thread %s", SimpleDateFormat.getDateTimeInstance().format(now), this.name, Thread.currentThread().getName())); try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
同时,在JobTest中创建多个job,并且让Scheduler去执行:
public static void main(String[] args) { Scheduler scheduler = new Scheduler(); Job job1 = new SimpleJob("job1"); scheduler.schedule(job1, System.currentTimeMillis(), 5000); Job job2 = new SimpleJob("job2"); scheduler.schedule(job2, System.currentTimeMillis() + 1000, 5000); Job job3 = new SimpleJob("job3"); scheduler.schedule(job3, System.currentTimeMillis() + 2000, 5000); Job job4 = new SimpleJob("job4"); scheduler.schedule(job4, System.currentTimeMillis() + 3000, 5000); Job job5 = new SimpleJob("job5"); scheduler.schedule(job5, System.currentTimeMillis() + 4000, 5000); scheduler.start(); }
再运行:
2011-10-14 22:22:01: job2 executed by thread Thread-0
2011-10-14 22:22:11: job3 executed by thread Thread-0
2011-10-14 22:22:21: job4 executed by thread Thread-0
2011-10-14 22:22:31: job5 executed by thread Thread-0
2011-10-14 22:22:41: job1 executed by thread Thread-0
......
可以看到,虽然我设置的job运行间隔都是5秒,但由于job本身要执行10秒,同时有多个job在排队执行,实现上job1的间隔已经到了50秒才执行。这样肯定是不行的。
那么,使用多线程应该就可以解决这个问题了,加入线程池。让每个job都由线程池里的一个线程去执行。
Scheduler源代码里,执行Job的方法是这样的:
/** * Run scheduler. */ public synchronized void run() { while (!shutdown) { if (jobList.isEmpty()) { linger(); } else { ScheduledJobEntry sje = (ScheduledJobEntry) jobList.get(0); long now = System.currentTimeMillis(); if (now >= sje.desiredExecutionTime) { executeInABox(sje.job); jobList.remove(0); if (sje.period > 0) { sje.desiredExecutionTime = now + sje.period; schedule(sje); } } else { linger(sje.desiredExecutionTime - now); } } } // clear out the job list to facilitate garbage collection jobList.clear(); jobList = null; System.out.println("Leaving scheduler run method"); } /** * We do not want a single failure to affect the whole scheduler. * @param job job to execute. */ void executeInABox(final Job job) { try { job.execute(); } catch (Exception e) { System.err.println("The execution of the job threw an exception"); e.printStackTrace(System.err); } }
可以看到,只要在executeInABox的方法里,使用线程池的线程来执行job,就可以了。现在加一个Scheduler的子类,我加上一个ExecutorService来实现线程池。同时我重写了executeInABox的方法,使用一个Runnable的实现类JobThread来运行job的execute方法。
package cn.lettoo.scheduler; import java.util.concurrent.ExecutorService; public class ThreadPoolScheduler extends Scheduler { private ExecutorService pool; public ThreadPoolScheduler(ExecutorService pool) { super(); this.pool = pool; } @Override void executeInABox(final Job job) { pool.execute(new JobThread(job)); } class JobThread implements Runnable { private Job job; public JobThread(Job job) { this.job = job; } public void run() { try { this.job.execute(); } catch (Exception e) { System.err.println("The execution of the job threw an exception"); e.printStackTrace(System.err); } } } }
再修改JobTest:
// 创建一个可缓存的线程池 ExecutorService pool = Executors.newCachedThreadPool(); // 构造带线程池的Scheduler ThreadPoolScheduler scheduler = new ThreadPoolScheduler(pool); ....... scheduler.start();
再运行,结果如下:
2011-10-14 22:37:51: job2 executed by thread pool-1-thread-2
2011-10-14 22:37:52: job3 executed by thread pool-1-thread-3
2011-10-14 22:37:53: job4 executed by thread pool-1-thread-4
2011-10-14 22:37:54: job5 executed by thread pool-1-thread-5
2011-10-14 22:37:55: job1 executed by thread pool-1-thread-6
可以看到,这时,job已经按我的要求,每5秒运行一次了。
但再仔细一想,如果job是有状态的,我的job运行要10秒,而5秒就要再运行一次,有时我们是需要一个job完全执行完才能下一次再执行的,比如上面的job1,第一次运行完,才可以执行第二次。
怎么解决这个问题?我目前的做法是在ThreadPoolScheduler里增加一个Set,存储正在执行的Job,当Job执行完成后,从这个Set中删除。在下次执行的时候,判断是否在Set中,如果在,则不执行。
private Set<Job> runningJobList = new HashSet<Job>(); @Override void executeInABox(final Job job) { if (!runningJobList.contains(job)) { runningJobList.add(job); pool.execute(new JobThread(job)); } } class JobThread implements Runnable { private Job job; public JobThread(Job job) { this.job = job; } public void run() { try { this.job.execute(); synchronized (this) { runningJobList.remove(job); } } catch (Exception e) { System.err .println("The execution of the job threw an exception"); e.printStackTrace(System.err); } } }
再执行:
2011-10-14 23:29:28: job2 executed by thread pool-1-thread-2
2011-10-14 23:29:29: job3 executed by thread pool-1-thread-3
2011-10-14 23:29:30: job4 executed by thread pool-1-thread-4
2011-10-14 23:29:31: job5 executed by thread pool-1-thread-5
2011-10-14 23:29:38: job2 executed by thread pool-1-thread-2
2011-10-14 23:29:40: job4 executed by thread pool-1-thread-4
2011-10-14 23:29:41: job5 executed by thread pool-1-thread-5
2011-10-14 23:29:42: job1 executed by thread pool-1-thread-3
2011-10-14 23:29:44: job3 executed by thread pool-1-thread-1
可以看到,这里已经避免了job在执行的时候,再次被执行。当然,也发生了其他的问题,如job1,第一次执行在23:29:27,执行过程是10秒,那应该在23:29:37执行完,而我们要求是每5秒执行一次的话,则应该立即执行才对,可是实际上是在23:29:42才执行的。为什么会这样呢?原来,在Scheduler中的run()方法中,只要执行了executeInABox方法之后,都会在jobList.remove(0),也就是在job1被scheduler并且到了时间之后,即使没有被执行,但是也被从jobList里remove掉了,然后再重新加5秒再次scheduler上,也就是在23:29:37秒job1真正执行完成时,才再次重新scheduler上,也就是在42秒执行了。这是一个问题,如果要实现这个问题,需要重新对Scheduler的代码进行重构,即在run()方法加上对runningJobList的检查功能。我这里就没有实现,如果您有更好的方法,欢迎指出。
- scheduler.rar (4.8 KB)
- 下载次数: 10
发表评论
-
ibookmark.me上线了!
2012-04-10 23:47 1490记得上次是2009年,接触到了python,gae,于是乎动手 ... -
使用virtualenv开发django应用
2012-01-12 11:03 1733Virtualenv是一个非常好的virtual python ... -
在Amazon EC2上试用play framework
2011-11-11 17:00 1366几个月以前,我在 ... -
一个Log生成工具小项目的实现
2011-11-01 17:32 1325这两天的主要工作是用java写一个log生成工具,用于 ... -
自定义log4j生成的log文件名
2011-11-01 13:57 2934很多时候,log4j的RollingFileAppen ... -
我和java操作数据库那些事儿(5)
2011-10-26 16:56 1612引用 半自动化武器来了:Spring JdbcTemplate ... -
我和JAVA数据库操作的那些事儿(4)
2011-10-24 16:21 1471通过前面几篇的介绍,对于JDBC的使用应该基本上够上项目开发的 ... -
我和JAVA数据库操作的那些事儿(3)
2011-10-20 15:35 2453在前面的两篇文章中,第一篇主要是讲了在jdbc编程中容 ... -
我和JAVA数据库操作的那些事儿(2)
2011-10-20 11:15 1530摘要 写道 上一篇提到的几个问题,在本篇有具体的代码。本篇后 ... -
图说事务隔离级别
2011-10-19 21:38 1385我们经常说的事务隔离级别,一般指的是SQL-92 ... -
我和JAVA数据库操作的那些事儿(1)
2011-10-19 15:26 2199摘要 我开始接触jdbc的 ... -
复习:观察者模式
2011-10-17 17:02 955观察者模式(有时又被称为发布/订阅模式)是软件设计模式 ... -
复习:代理模式
2011-10-17 15:53 696代理模式是常用的Java 设计模式,它的特征是代理类与 ... -
Spring RMI 简单实现
2011-10-14 13:47 1017好久没有写java代码了,最近工作项目上需要做一个 ... -
一道关于树的面试题
2011-10-13 15:31 908记得不久以前有道面试题,要求下面的数据结构 ... -
一个简单的JAVA后台程序框架
2011-10-13 09:31 1720本项目已经通过git进行版本管理,checkout ... -
测试驱动开发:红、绿、重构
2011-10-12 23:01 859在读Ruby on Rails Tutorial: ... -
Spring JMS和ActiveMQ的应用
2011-10-12 22:43 2009笔者近期参与一个分析log的项目。主要流程是:读取Lo ...
相关推荐
Chainsaw_oi Gui cutter oi
chainsaw软件-log4j日志查看软件
Tomcatlog4j日志文件 log4j-chainsaw-1.3alpha-3.jar
Chainsaw_1 Cutter1 asli
Chainsaw Gui CUTTER asli
octo-chainsaw-源码.rar
BitDefender.2011.Patch.3.1&Chainsaw_1
Chainsaw 的开发位于 https://bitbucket.org/ignazio1977/chainsaw 如需最近的源代码和... Chainsaw 使用模块化和原子分解将推理任务委托给其他 OWL 2 推理器; 它利用分而治之来将本体减少到回答特定查询所需的部分。
CSF405 EN 2001673 P0802183-01, Rev B CHAINSAW (TOP HANDLE) OPERATOR MANUAL
Chainsaw是一个Log4J软件包的GUI日志查看器和过滤器。 它侦听使用SocketAppender发送的LoggingEvent对象,并将它们显示在表中。 可以根据优先级,线程名称,类别名称和消息来过滤事件。 它可以
python库,解压后可用。 资源全名:git_chainsaw-0.1.11-py3-none-any.whl
资源来自pypi官网。 资源全名:git_chainsaw-0.1.11-py3-none-any.whl
电锯 电锯-日志数据生成器 脚本-Shell脚本日志消息生成器-演示
redesigned-octo-chainsaw
probable-chainsaw:参考
一个新的Flutter项目。 入门 该项目是Flutter应用程序的起点。 如果这是您的第一个Flutter项目,那么有一些资源可以帮助您入门: 要获得Flutter入门方面的帮助,请查看我们的,其中提供了教程,示例,有关移动开发...
ChainsawGeometric
相反,请在您的计算机上创建一个新的空目录,然后执行git init (或在Github上创建一个空的repo并将其克隆到您的本地计算机上) 现在,您将必须添加fs-app-template作为远程服务器并将其合并到您自己的存储库中。...
设置在application / configs中创建一个名为secret-config.json 。 您可以按照application / configs / example-config.json进行操作。 配置: ethNodeAddress:您要连接的以太坊节点的IP地址和端口testNodeAddress...