两个线程之间资源的同步访问

时间:2017-12-19 06:44:04

标签: java multithreading

我有两个线程和一个缓存。让我们将线程1称为Tb,它是后台线程(即具有较低优先级)而另一个线程称为Tm(主线程具有较高优先级)。两个线程都有一个缓存可以更新。你可以说线程Tb对Tm来说是一种帮助,可以在可以的情况下抢先填充缓存。

当Tm想要访问缓存时,它必须立即访问它,因为某些UI更新显示取决于它。

由于缓存是为并发写入共享的,因此我按如下方式同步了对缓存的访问:

Element checkAndUpdateCache(int elementPositionToBeChecked){

  Element toBeReturned;

  synchronized(lock){

    // Check if the element is already present in the cache
    if(!cache.hasElement(elementPositionToBeChecked)){

      // If not, retrieve a new one and fill the cache
      toBeReturned = retrieveNewElement(elementPositionToBeChecked);
      cache.put(elementPositionToBeChecked, toBeReturned );
    }
    else{
      toBeReturned = cache.getElement(elementPositionToBeChecked);
    }

  }

  return toBeReturned;

}

问题在于,由于后台线程在循环中调用此方法,因此需要非常快速地继续访问缓存,并且几乎从不放弃锁定。目前,我在每个循环周期之后调用Thread.yield(),另外还调用Thread.sleep(10)以提供对主线程的一些访问。

具有不同的优先级并没有真正帮助,也没有在每个循环周期调用Thread.yield()。 Thread.sleep()确实有所帮助,但我认为,我们都同意,这根本不是一个好策略。毕竟,我们想要最大的CPU利用率,对吗?

有没有办法确保无论何时主线程需要访问缓存,它都会在后台线程等待它时轻松获取它并稍后恢复操作?

编辑:实施细节

缓存为Map<Integer, Album>,其中密钥为Integer

public static Album getAlbum(Context context, int position, @NonNull Cursor cursor, @NonNull Map<Integer, Album> cache){

        // Do we have the Album in cache
        Album albumInfo = cache.get(position);

        if(albumInfo == null){

            cursor.moveToPosition(position);

            // Let's cache this Album
            albumInfo = Album.fromMediaStoreCursor(context, cursor);
            cache.put(position, albumInfo);
        }
EDIT 2
        return albumInfo;
    }

编辑2 :限制的后台线程循环

// While we pre-emptively fetch the Albums to cache in the background :)
if (cursorImages != null) {

    for (int i = 0; i < cursorImages.getCount(); i++) {

        synchronized(SnapsboardApplication.getInstance()) {
            AlbumsListCursorAdapter.getAlbum(ListPhotoVideoAlbumsOnDeviceActivity.this,
                i, cursorImages, cache);
        }

        // Keep checking if we have been asked to cancel
        if (isCancelled()) {
            return null;
        }

        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Thread.yield();
    }
}

2 个答案:

答案 0 :(得分:0)

如果不了解完整的实施方式,很难提供真正的好建议,但我想到的可能解决方案是将地图的定义更改为Map<Integer, Future<Album>>并使用{{ 1}}检索要添加到地图的数据。这样,后台线程不应该在方法ExecuterService中保持太久(我认为这是你的主线程阻塞的原因)并且你的主线程已经获得了结果,即使检索是尚未完成。调用retrieveNewElement(elementPositionToBeChecked)将返回所需的值,或者将阻塞直到检索完成(或达到超时)。

答案 1 :(得分:0)

Thread Tb是在后台准备数据的助手。如果必须保留线程Tb,则需要一种机制来通知线程Tb停止/恢复工作(此外,通知线程Tb准备哪些数据)。

丑陋,宣布volatile boolean work

Thread Tm。输入checkAndUpdateCache

...
work = false;
synchronized(lock) {
    ...
    work = true;
    lock.notify();
}

线程Tb。输入checkAndUpdateCache

...
synchronized(lock) {
    while (!work) {
        lock.wait();
    }
    ...
}

但是,我不认为这个问题只是问题。例如:

  • Tb如何决定是继续准备数据还是准备哪些数据。
  • 线程Tb / Tm需要不同的功能入口做不同的事情。
  • 必须致电retrieveNewElement。也许release lock, retrieveNewElement, relock and put会更好。