ThreadPool中的自定义任务顺序

时间:2012-02-05 14:45:24

标签: java android concurrency threadpool

我目前使用FixedThreadPool从网上下载图片,如下所示:

ExecutorService mThreadPool = Executors.newFixedThreadPool(MAX_THREADS);

然后我只提交带有图片Runnable的新URL,该图片正在从URL下载图片,或者是否存在于缓存中,从那里加载图像。

我希望能够确保一次只有一个线程可以处理特定的URL(以防止图像下载{1}}次的情况下的静止,并且如果该线程线程完成并下载该图像我想允许下一个(或全部,具有相同的URL)运行,从我的缓存中加载以前下载的图像。

这就是我所说的简单(我希望如此)方案:http://i43.tinypic.com/xnz3f9.jpg

我已经看到一些自定义实现MAX_THREADSRunnable任务的示例,但所有这些都需要我在执行这些任务之前知道所有URL,我想在ListView中动态使用它加载内容,以便无法选择。

感谢您的帮助。 :)

1 个答案:

答案 0 :(得分:3)

你将不得不实现某种同步方案(锁定),以防止第二个线程开始下载另一个线程已经下载的同一个文件。

我们想到的一个解决方案是将Runnable构造函数的每个引用传递给Map<String,Lock>ReentrantLock,以便同步对它的访问。将文件名作为键放在地图中,同时将其与另一个ReentrantLock一起下载,其他线程将等待作为值。

在下载文件之前锁定并检查Map

如果那里没有条目,您可以创建一个新的ReentrantLock并插入Map。然后解锁Map本身的锁。完成下载文件后,再次锁定地图,从Map移除锁定并解锁,然后解锁地图。

如果那里有一个条目,你知道另一个线程正在下载该文件,你有一个锁可以使用。解锁地图,锁定文件锁,等待锁定。当你获得锁定时,你知道另一个线程已经完成。

示例:

...
mapLock.lock();
Lock fileLock = fileMap.get(fileName);
if (fileLock == null)
{
    fileLock = new ReentrantLock();
    fileLock.lock();
    fileMap.put(fileName, fileLock);
    mapLock.unlock();    

    // download and deal with file

    mapLock.lock();
    fileMap.remove(fileName);
    fileLock.unlock();
    map.unlock();
}
else // someone is downloading this file!
{
    mapLock.unlock();
    fileLock.lock();
    fileLock.unlock(); 

    // When you get here, you know the other thread has downloaded the file
    // do whatever it is you need to do in that case
}

更优雅的解决方案是使用ConditionReentrantLock(为了简洁,我不包括吸气剂)

public class LockSet {
    public Lock lock;
    public Condition condition;

    public LockSet() {
        lock = new ReentrantLock();
        condition = lock.newCondition();
    }
}

现在有了这个方便的课程,您可以执行以下操作:

mapLock.lock();
LockSet lockSet = fileMap.get(fileName);
if (lockSet == null)
{
    lockSet = new LockSet();
    fileMap.put(fileName, lockSet);
    mapLock.unlock();

    // download and deal with file

    mapLock.lock();
    fileMap.remove(fileName);
    lockSet.lock.lock();
    lockSet.condition.signalAll();
    mapLock.unlock();
}
else // someone is downloading
{
    lockSet.lock.lock();
    mapLock.unlock();
    lockSet.condition.await();

    // once we get here, the file has finished downloading in the other thread
}

编辑:其作者删除的答案让我思考了一下这个问题。

此方案的一个缺点是池中的线程偶尔会等待。由于您使用固定池大小,如果同时对同一文件有多个请求,则可能会导致瓶颈情况。如果您要在帖子中提到的那样为任务实现排队机制,那么实际上只需要使用Map本身(Map<String,String>)而不使用双重锁定机制。

您的工作线程会将文件信息从队列中拉出来,检查映射是否存在(仍然锁定/解锁映射锁定),但是如果其他人正在下载它,则将文件放回队列中(仅表示Map中存在该文件的条目 - 您可以使用null作为值)