多线程并发及并发(一)

Timer线程介绍

Timer 是一个用来将将要执行的任务放在后台线程执行的工具。被执行性的任务可以执行一次就结束了,也可以通过设定的时间间隔来周期性的执行。

Timer的基本使用

创建一个 Timer 对象,调用其 schedule() 方法传递一个TimerTask ,同时设定多长时间后开始执行及执行周期。这样,就可以执行任务了。代码如下:

   public void test() {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                while (true) {
                    System.err.println(Thread.currentThread().getName());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, 10000);
    }

Timer 的源码分析

Timer使用涉及的三个相关类

  • TimerThread 真正执行任务的线程,Thread 的子类
  • TimerTask 被执行的任务,可以看做一个Runnable 对象
  • TaskQueue 存放 TimerTask 的类,封装了一个 TimerTask 数组用来保存待执行的任务

创建Timer对象

Timer timer = new Timer();

分析 Timer 的源码,可以看到,Timer 对象分装了 TaskQueue对象和 TimerThread对象

public class Timer {
	private final TaskQueue queue = new TaskQueue();
	
	 private final TimerThread thread = new TimerThread(queue);
	//省略其他代码
}

在通过构造方法创建对象时,最中会调用如下构造方法,可以看出,对象创建过程中就开启了线程。线程的 start() 方法最终会调用其 run() 方法。

  public Timer(String name, boolean isDaemon) {
        thread.setName(name);
        thread.setDaemon(isDaemon);
        thread.start();
  }

由于 TimerThread 继承了 Thread ,同时重写了父类的 run() 方法,所以最终会执行 TimerThreadrun() 方法。

class TimerThread{
	 private TaskQueue queue;

    TimerThread(TaskQueue queue) {
        this.queue = queue;
    }
    public void run() {
        try {
            mainLoop();
        } finally {
            // Someone killed this Thread, behave as if Timer cancelled
            synchronized(queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  // Eliminate obsolete references
            }
        }
    }
}

run() 方法会调用 mainLoop() 方法,这才是任务的核心代码。

/**
     * The main timer loop.  (See class comment.)
     */
    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();
                    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) {
            }
        }
    }

我们可以看到,mainLoop() 方法里面有个死循环,同时对queue对象进行同步加锁 synchronized ,先判断任务列表 queue 是否为空,如果为空,则调用 queue.wait() 使当线程进行休眠状态。所以说,如果不向 queue 中添加任务,那么该线程将永远处于休眠状态。那么如何添加任务呢? timer.schedule() 方法的作用就出来了。

添加任务

timer.schedule(TimerTask,long) ,最终会调用 sched() 方法。
添加任务的方法

private void sched(TimerTask task, long time, long period) {
        if (time < 0)
            throw new IllegalArgumentException("Illegal execution time.");

        // Constrain value of period sufficiently to prevent numeric
        // overflow while still being effectively infinitely large.
        if (Math.abs(period) > (Long.MAX_VALUE >> 1))
            period >>= 1;

        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();
        }
    }

添加任务过程中先进行各种条件的判断,同样也对 queue 对象进行了加锁,调用 queue.add(task) 将任务添加到任务列表中。那么任务添加了,如何执行呢? queue.notify() 会唤醒其他享有该对象的锁的的线程继续执行任务。我们可以看到另一个对该对象加锁的线程正是TimerThread ,而此时 TimerThread 又调用了 wait() 方法处于等待状态,此时 notify() 方法会通知它继续执行任务。

执行任务

       再次回到TimerThreadmainLoop() 方法中,当线程被唤醒后,继续执行字后的代码。同时进行条件判断,如果被唤醒后线程列表还是为空,那么线程到此结束。

       从线程列表中取出线程,查看其执行时间及是否需要周期性的执行。进行一系列操作后,开始执行任务,调用 task.run() 方法,这样,我们创建的 TimerTask 对象的 run() 方法就会执行了。

       当我们不需要执行任务后,需要调用 timercancel() 方法取消任务执行。

  public void cancel() {
        synchronized(queue) {
            thread.newTasksMayBeScheduled = false;
            queue.clear();
            queue.notify();  // In case queue was already empty.
        }
    }

使用注意

  1. 通常我们在Android中会周期性的执行一些任务,当这些任务有和界面进行交互的功能时,我们退出界面前一定要先取消任务,不然会引发异常,导致程序挂掉。
  2. 当项目中存在大量的定期任务时,可以考虑使用Quartz
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章