我有两个线程和一个缓存。让我们将线程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();
}
}
答案 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();
}
...
}
但是,我不认为这个问题只是问题。例如:
retrieveNewElement
。也许release lock, retrieveNewElement, relock and put
会更好。