標籤:
當我們的ListView中的Item包含圖片,而且這些圖片是同一資源,我們用多線程去載入圖片,這時候可能就發生了這種情況。
比如線程是人,第一個人去做載入圖片到緩衝的工作,還沒做好時第二個人要這同一張張圖,結果緩衝還沒有這張圖,於是第二個人也去載入圖片到緩衝。。以此類推,同時可能有N個人在載入同一張圖,N取決於圖片沒載入到緩衝前,可見的Item數量。
於是針對這種情況進行細化同步,避免發生。
//首先需要一個ConcurrentHashMap<String, Boolean>,String表示資源地址片地址,Boolean表示是否有線程正在載入。//之所以用ConcurrentHashMap,是因為它不但是同步的而且效率高於Synchronized和ReenTrantLock.private ConcurrentHashMap<String, Boolean> mConcurentMap;//然後需要一個ReentrantLock鎖住線程內的關鍵判斷語句。private ReentrantLock mLock;
當一個線程沒有在緩衝找到資源,那麼將讀取資源,ConcurrentHashMap記錄這個資源地址,並且Boolean為true,表示有人在幹這個活了,下面的人只要等待幹活的人幹完就行了。
//這是載入圖片的線程,當緩衝中沒找到圖片(mBitmapCache.get(path) == null)進入此線程Thread thread = new Thread() { @Override public void run() { mLock.lock();//鎖住關鍵判斷邏輯,避免同一資源多個線程都進入false邏輯(載入圖片) if (mConcurentMap.get(path) == false) { // mConcurentMap添加索引值對,表示path這個資源已經有人在載入了 mConcurentMap.put(path, true); mLock.unlock(); //釋放鎖,下面進入的線程只能進入true的邏輯(等待第一個人做完工作) if (path != null && (!path.equals("null")) && (!"".equals(path))) { //載入圖片 item.bm = PictureHelper.getCropImage(path, 400, true, 100,mActivity, 7, true); //圖片載入完放入緩衝Lrucache mBitmapCache.put(path, item.bm); //設定索引值對,false表示path這個資源已經載入完,或者沒人載入了 mConcurentMap.put(path, false); } else { item.bm = null; //圖片地址無效 mConcurentMap.put(path, false); } Message msg = new Message(); msg.what = 1; handler.sendMessage(msg); } else { // 釋放鎖 mLock.unlock(); // 表示有人在載入圖片,因此用死迴圈等待載入完成 while (true) { // false表示載入完成,此時緩衝已有圖片,應該跳出迴圈 if (mConcurentMap.get(path) == false) { // Logg.D("other need break cycle"); break; } } // Logg.D("other go out cycle"); if (mBitmapCache.get(path) != null) { item.bm = mBitmapCache.get(path); Message msg = new Message(); msg.what = 2; handler.sendMessage(msg); } else { throw new NullPointerException("(mBitmapCache.get(path) == null!"); } } } }; executors.execute(thread);
Android ListView避免多線程載入一個同一資源