为了利用线程池中的线程来执行任务,我们需要将要执行的任务添加到线程池所维护的任务队列中。当线程池中的某个线程可用时,ThreadPoolExecutor就会从任务队列中取出一个任务交给该线程来执行。同时,当我们启动了一个任务后随后发现该任务不再需要时,我们还可以通过取消线程来停止线程池中的线程正在运行的任务。
任务交由线程池中的线程来执行
通过将一个任务Runnable对象传递给ThreadPoolExecutor类的execute()方法,即可启动线程池中的一个可用线程来执行该任务。该方法首先将这个任务加入到线程池的工作队列中,当线程池中有可用线程时,ThreadPoolExecutor就会取出任务队列中等待时间最长的任务交给该线程来执行。下面的例子很好地说明了这一点:
public class PhotoManager
{
public void handleState(PhotoTask photoTask, int state)
{
switch (state)
{
//前一个任务执行完成后,本例中为下载完网络图片
case DOWNLOAD_COMPLETE:
//启动对下载完的图片的解码任务
mDecodeThreadPool.execute(
photoTask.getPhotoDecodeRunnable());
...
}
...
}
...
}
当ThreadPoolExecutor把一个任务Runnable对象交给一个线程来执行时,它会自动调用该任务对象的run()方法。
中断任务的执行
为终止一个任务,我们需要中断运行该任务的线程。为实现这一点,我们需要在创建任务时保存一个对执行该任务的线程的引用。如下面的例子所示:
class PhotoDecodeRunnable implements Runnable
{
public void run()
{
/*
* 将该任务执行时启用的线程引用保存下来
* 后面就可以通过mPhotoTask.getImageDecodeThread()
* 来获取到执行该任务的线程的引用
*/
mPhotoTask.setImageDecodeThread(Thread.currentThread());
...
}
...
}
对线程的中断是通过调用Thread类的interrupt()方法来实现的。需要注意的是,线程对象是由系统来维护的,因此线程对象是有可能在我们应用的进程之外被系统修改。基于这一点,在我们中断一个线程之前需要对该线程加锁,这是通过synchronized同步块来实现的,如下面的例子所示:
public class PhotoManager
{
public static void cancelAll()
{
/*
* 创建线程池任务队列的副本
*/
Runnable[] runnableArray = new Runnable[mDecodeWorkQueue.size()];
mDecodeWorkQueue.toArray(runnableArray);
int len = runnableArray.length;
/*
* 将对线程的中断操作放在一个同步块中
*/
synchronized (sInstance)
{
for (int runnableIndex = 0; runnableIndex < len; runnableIndex++)
{
//获取任务所在线程的引用
Thread thread = runnableArray[taskArrayIndex].mThread;
//线程存在则中断它
if (null != thread)
{
thread.interrupt();
}
}
}
}
...
}
在多数情况下,Thread.interrupt()会立即停止该线程的运行。然而,这种中断只能停止处于等待状态的线程,而不会中断那些非常占CPU或网络密集型的任务。为避免降低系统的运行速度或锁住系统,在使用当前线程去执行一个操作前我们需要测试是否还有挂起的中断请求,如下面的代码所示:
/*
* 在继续操作之前,检测当前线程是否被中断,若是被中断则不再继续后面的操作
*/
if (Thread.interrupted())
{
return;
}
...
//当前线程未被中断则可继续执行操作(通常为一个非常占用CPU的操作)
BitmapFactory.decodeByteArray(
imageBuffer, 0, imageBuffer.length, bitmapOptions);
...