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

问题描述 投票:1回答:2

我有两个线程和一个缓存。让我们将线程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();
    }
}
java multithreading
2个回答
0
投票

在不知道完整实现的情况下,很难给出真正好的建议但是我想到的可能的解决方案是将地图的定义更改为Map<Integer, Future<Album>>并使用ExecuterService来检索要添加到地图中的数据。这样,后台线程不应该在方法retrieveNewElement(elementPositionToBeChecked)(我假设你的主线程阻塞的原因)中保持太长时间,并且你的主线程已经获得了结果,即使检索还没有完成。调用future.get(timeout, TimeUnit.SECONDS)将返回所需的值或将阻塞直到检索完成(或达到超时)。


0
投票

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

丑陋,宣布一个volatile boolean work

线程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会更好。
© www.soinside.com 2019 - 2024. All rights reserved.