Timer 和TimerTask 定时任务是否多线程
2013-05-16 18:01
405 查看
今天一个同事问过我这样一个问题。
Timer 启动一个任务,每个1秒钟执行一次 。如果第一次执行这个任务Task需要5秒。
那么第二次执行这个任务是在第2秒开始,还是在5秒开始。
问题就此开始了。
(1)如果是在第2秒执行这个任务。那么Task中的数据在同一个时间有两个线程在操作。这种操作
造成显现就会出现数据混乱。线程不安全。两个线程同时拿到一个全局变量index=1,先后加1.结果变成3.
第一个线程打印2,第二个线程打印3。这个现象就会造成线程不安全。(jvm现实应该没有这么傻。)
(2)所有可能性是第二次执行这个任务是从第5秒开始。它会等第一次执行完后在开始执行。
测试代码:
这还不能充分证明我的猜想。
下面来看一下Timer和TimerTask的部分源码。
这个时候会创建TimerThread(queue)对象。
TaskQueue队列是用来存放任务Task,可以多个,先进先出。只有等第一个执行完才能执行第二个依次执行。
TimerThread是用来控制Task任务执行。继续Thread类。先看一下TimerThread类。
来分析一下这代码,请注意我标红的代码。在创建Timer对象的时候,同时会初始化TimerThread ,并执行mainLoop这个方法。
这个时候queue对象被创建,但是queue对象没有元素。所以会执行queue.wait()。代码停止继续往下执行。queue等待被唤醒。
在执行Timer.schedule()方法中会调用sche()方法。此时会向queue中添加Task任务。 queue.add(task);并且唤醒queue对象queue.notify()。
回到mainLoop()方法,queue.notify()唤醒后将继续往下执行while中的代码,代码判断task任务状态是否被取消Task.cancelled。(可以通过Task.cancelled()方法取消)。
如果没有取消,在判断执行的时间。executionTime<currentTime 就执行task.run 启动任务。(注意task继承Runnable ,但是task.run并不是开启一个线程任务。只是普通
对象方法调用。这个线程是TimerThread开启的。)
如果executionTime>currentTime,那么queue.wait(executionTime-currentTime)。
说了这么半天,不知道大家明白了没有Timer.scheduel(task,1000,1000)在第二次调用任务是从第5秒开始。
那是因为Time.scheduel启动定时任务,并不是多线程。而是单线程在while循环调用task.run。所以第二次调用必须等待
第一次执行完毕或才能够继续往下执行。
Timer 启动一个任务,每个1秒钟执行一次 。如果第一次执行这个任务Task需要5秒。
那么第二次执行这个任务是在第2秒开始,还是在5秒开始。
问题就此开始了。
(1)如果是在第2秒执行这个任务。那么Task中的数据在同一个时间有两个线程在操作。这种操作
造成显现就会出现数据混乱。线程不安全。两个线程同时拿到一个全局变量index=1,先后加1.结果变成3.
第一个线程打印2,第二个线程打印3。这个现象就会造成线程不安全。(jvm现实应该没有这么傻。)
(2)所有可能性是第二次执行这个任务是从第5秒开始。它会等第一次执行完后在开始执行。
测试代码:
public class Task extends TimerTask { int index = 0; public void run() { try { for (int i = 0; i < 5; i++) { System.out.println(++index+"Thread "+this.scheduledExecutionTime()); Thread.sleep(999);//休息1秒 } } catch (Exception e) { e.printStackTrace(); } } public static void main(String args[]) { Timer timer = new Timer(); Task task = new Task(); timer.schedule(task, 1000,1000); } }打印结果:
1ThreadName1368694219239 2ThreadName1368694219239 3ThreadName1368694219239 4ThreadName1368694219239 5ThreadName1368694219239 6ThreadName1368694224235 7ThreadName1368694224235 8ThreadName1368694224235 9ThreadName1368694224235 10ThreadName1368694224235
这还不能充分证明我的猜想。
下面来看一下Timer和TimerTask的部分源码。
public class Timer { /** * The timer task queue. This data structure is shared with the timer * thread. The timer produces tasks, via its various schedule calls, * and the timer thread consumes, executing timer tasks as appropriate, * and removing them from the queue when they're obsolete. */ private TaskQueue queue = new TaskQueue(); /** * The timer thread. */ private TimerThread thread = new TimerThread(queue);在代码里我们经常创建一个Timer对象。Timer timer=new Timer();
这个时候会创建TimerThread(queue)对象。
TaskQueue队列是用来存放任务Task,可以多个,先进先出。只有等第一个执行完才能执行第二个依次执行。
TimerThread是用来控制Task任务执行。继续Thread类。先看一下TimerThread类。
public void run() { try { mainLoop(); } finally { // Someone killed this Thread, behave as if Timer cancelled synchronized(queue) { newTasksMayBeScheduled = false; queue.clear(); // Eliminate obsolete references } } }这里继承Thread类并实习run方法。这个主要关注在run方法中 mainLoop();
private void mainLoop() { while (true) { try { TimerTask task; boolean taskFired; synchronized(queue) { // Wait for queue to become non-empty while (queue.isEmpty() && newTasksMayBeScheduled) queue.wait();//queue对象元素个数为空。等待被唤醒。 if (queue.isEmpty()) break; // Queue is empty and will forever remain; die // Queue nonempty; look at first evt and do the right thing long currentTime, executionTime; task = queue.getMin(); synchronized(task.lock) { if (task.state == TimerTask.CANCELLED) { queue.removeMin(); continue; // No action required, poll queue again } currentTime = System.currentTimeMillis(); executionTime = task.nextExecutionTime; if (taskFired = (executionTime<=currentTime)) { if (task.period == 0) { // Non-repeating, remove queue.removeMin(); task.state = TimerTask.EXECUTED; } else { // Repeating task, reschedule queue.rescheduleMin( task.period<0 ? currentTime - task.period : executionTime + task.period); } } } if (!taskFired) // Task hasn't yet fired; wait queue.wait(executionTime - currentTime); } if (taskFired) // Task fired; run it, holding no locks task.run(); } catch(InterruptedException e) { } } } }
来分析一下这代码,请注意我标红的代码。在创建Timer对象的时候,同时会初始化TimerThread ,并执行mainLoop这个方法。
这个时候queue对象被创建,但是queue对象没有元素。所以会执行queue.wait()。代码停止继续往下执行。queue等待被唤醒。
在执行Timer.schedule()方法中会调用sche()方法。此时会向queue中添加Task任务。 queue.add(task);并且唤醒queue对象queue.notify()。
回到mainLoop()方法,queue.notify()唤醒后将继续往下执行while中的代码,代码判断task任务状态是否被取消Task.cancelled。(可以通过Task.cancelled()方法取消)。
如果没有取消,在判断执行的时间。executionTime<currentTime 就执行task.run 启动任务。(注意task继承Runnable ,但是task.run并不是开启一个线程任务。只是普通
对象方法调用。这个线程是TimerThread开启的。)
private void sched(TimerTask task, long time, long period) { if (time < 0) throw new IllegalArgumentException("Illegal execution time."); synchronized(queue) { if (!thread.newTasksMayBeScheduled) throw new IllegalStateException("Timer already cancelled."); synchronized(task.lock) { if (task.state != TimerTask.VIRGIN) throw new IllegalStateException( "Task already scheduled or cancelled"); task.nextExecutionTime = time; task.period = period; task.state = TimerTask.SCHEDULED; } queue.add(task); if (queue.getMin() == task) queue.notify(); } }
如果executionTime>currentTime,那么queue.wait(executionTime-currentTime)。
说了这么半天,不知道大家明白了没有Timer.scheduel(task,1000,1000)在第二次调用任务是从第5秒开始。
那是因为Time.scheduel启动定时任务,并不是多线程。而是单线程在while循环调用task.run。所以第二次调用必须等待
第一次执行完毕或才能够继续往下执行。
相关文章推荐
- Java定时多线程任务实现(TimerTask)
- 多线程应用:用TimerTask于Timer任意时间调度任务
- spring中集成TimerTask执行定时任务
- spring中集成TimerTask执行定时任务
- Spring ScheduledTimerTask 定时任务执行
- Java 中Timer和TimerTask 定时器和定时任务使用的例子
- 项目启动后开启定时任务方法-->TimerTask中如何调用service
- spring中集成TimerTask执行定时任务
- java 定时任务 TimerTask Timer
- Java 中Timer和TimerTask 定时器和定时任务使用的例子
- Timer TimerTask 定时任务 Timer was canceled TimerTask is scheduled already
- java定时任务,每天定时执行任务(JDK TimerTask)
- android休眠之后 定时任务TimerTask不生效
- spring中集成TimerTask执行定时任务 中配置文件的配置
- Spring整合TimerTask实现定时任务调度
- 使用TimerTask做每月定时执行任务.
- Java 中Timer和TimerTask 定时器和定时任务使用的例子
- 使用jse的TimerTask实现在一天中的某个区间段定时任务
- java定时任务之Timer实现多线程任务