【问题标题】:Custom order of tasks within ThreadPoolThreadPool 中的自定义任务顺序
【发布时间】:2012-02-27 08:06:50
【问题描述】:

我目前使用FixedThreadPool从网上下载图片,像这样:

ExecutorService mThreadPool = Executors.newFixedThreadPool(MAX_THREADS);

然后我只需提交带有图像URL 的新Runnables,它要么从URL 下载图像,要么如果它存在于缓存中,则从那里加载它。

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

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

我已经看到了一些自定义实现 RunnableQueue 任务的示例,但是所有这些示例都需要我在执行这些任务之前知道所有 URL,我想在具有动态加载内容的 ListView 中使用它,以便选项将是不可能的。

感谢您的帮助。 :)

【问题讨论】:

    标签: java android concurrency threadpool


    【解决方案1】:

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

    想到的一个解决方案是向您的每个Runnables 构造函数传递对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 作为值)

    【讨论】:

      猜你喜欢
      • 2011-05-27
      • 2013-08-21
      • 1970-01-01
      • 2014-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-31
      • 1970-01-01
      相关资源
      最近更新 更多