我目前使用FixedThreadPool
从网上下载图片,如下所示:
ExecutorService mThreadPool = Executors.newFixedThreadPool(MAX_THREADS);
然后我只提交带有图片Runnable
的新URL
,该图片正在从URL
下载图片,或者是否存在于缓存中,从那里加载图像。
我希望能够确保一次只有一个线程可以处理特定的URL
(以防止图像下载{1}}次的情况下的静止,并且如果该线程线程完成并下载该图像我想允许下一个(或全部,具有相同的URL)运行,从我的缓存中加载以前下载的图像。
这就是我所说的简单(我希望如此)方案:http://i43.tinypic.com/xnz3f9.jpg
我已经看到一些自定义实现MAX_THREADS
和Runnable
任务的示例,但所有这些都需要我在执行这些任务之前知道所有URL,我想在ListView中动态使用它加载内容,以便无法选择。
感谢您的帮助。 :)
答案 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
}
更优雅的解决方案是使用Condition和ReentrantLock
(为了简洁,我不包括吸气剂)
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
作为值)