2017-12-19 16 views
1

두 개의 스레드와 캐시가 있습니다. 스레드 1을 Tb로 부르겠습니다. 백그라운드 스레드 (즉, 낮은 우선 순위)이고 다른 스레드는 Tm (우선 순위가 더 높은 주 스레드)입니다. 두 스레드에 의해 업데이트되는 캐시가 있습니다. 스레드 Tb가 가능할 때 캐시를 선제 채우기 위해 스레드 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 사용률을 원할 것입니다.

주 스레드가 캐시에 액세스해야 할 때마다 백그라운드 스레드가 스레드를 기다리고 조금 후에 작업을 다시 시작하는 동안 언제든지이를 확보 할 수있는 방법이 있습니까?

편집 : 구현 세부 사항

캐시 키가 Integer 곳의 Map<Integer, Album>입니다.

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(); 
    } 
} 
+1

구현 된 캐시 란 무엇입니까? 'java.util.concurrent' 컬렉션을 사용한다면 당신은 전혀 동기화 할 필요가 없습니다. – EJP

+0

'Tm'은 캐시 자체에 데이터를 추가합니까, 아니면 순전히 읽기 액세스입니까? – Lothar

+0

@EJP : 한 스레드가 pos p에 대해 캐시가 비어있는 것을 발견하고 캐시를 채우는 동안 주 스레드가 캐시를 검사하여 pos p를 찾은 다음 비어있는 것으로 판단하지만 곧 만만치 않다. 곧 –

답변

0

를 조절되는 백그라운드 스레드 루프 지도의 정의를 Map<Integer, Future<Album>>으로 변경하고 ExecuterService을 사용하여지도에 추가 할 데이터를 검색하십시오. 그렇게하면 백그라운드 스레드가 메소드 retrieveNewElement(elementPositionToBeChecked) (너무 길어서 메인 스레드가 블로킹을당하는 이유라고 가정)과 검색이 아직 완료되지 않은 경우에도 메인 스레드가 결과를 얻게됩니다. future.get(timeout, TimeUnit.SECONDS)을 호출하면 필요한 값이 반환되거나 검색이 끝날 때까지 (또는 제한 시간에 도달하면) 차단됩니다.

+0

오케이. 구현 세부 정보를 추가했습니다. 너가 어떤 광석 세부 사항을 필요로하면 나에게 알려줘. –

+0

@pulp_fiction 위에서 언급 한 배경 스레드 루프는 문제의 원인 인 것처럼 보이기 때문에 유용합니다. – Lothar

+0

오케이. 루프 추가 –

0

스레드 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이 더 나을 것입니다.