2011-12-21 6 views
0

캐시 공급자로 사용되는 클래스를 만들었습니다. Map, timestamped map 엔트리를 사용하며 매번 정리 작업을 수행하는 Thread를 생성합니다. 이 클래스는 웹 응용 프로그램에서 사용됩니다. 이 웹 응용 프로그램에는 POST에 30 초가 걸리는 문제가있었습니다. 이 캐시 클래스로 문제를 추적하여 문제를 해결했습니다.이 스레드 코드의 오류는 어디에 있습니까?

나는이 수업에서 오류를 찾기 위해 최선을 다했으나하지 못했습니다. 제발 도와주세요. 사용자 클래스는 사용자를 설명하는 일종의 POJO라고 가정합니다.

public class UserStore implements Thread.UncaughtExceptionHandler { 
private static volatile UserStore instance; 
private static Thread cleanUpThread; 
private static Map<String, TimeStampedToken<User>> tokenMap = new HashMap<String, TimeStampedToken<User>>(); 
public static UserStore getInstance() { 
    if (instance == null) { 
     synchronized(UserStore.class) { 
      if (instance == null) { 
       instance = new UserStore(); 
       cleanUpThread = new Thread(new CleanUpWorker()); 
       cleanUpThread.setUncaughtExceptionHandler(instance); 
       cleanUpThread.start(); 
      } 
     } 
    } 
    return instance; 
} 
public void uncaughtException(Thread thread, Throwable throwable) { 
    if (throwable instanceof ThreadDeath) { 
     cleanUpThread = new Thread(new CleanUpWorker()); 
     cleanUpThread.setUncaughtExceptionHandler(this); 
     cleanUpThread.start(); 
     throw (ThreadDeath)throwable; 
    } 

} 
private static class CleanUpWorker implements Runnable { 
    private static final long CLEANUP_CYCLE_MS = 300000; 
    private static final long OBJECT_LIVE_TIME = 299900; 
    public void run() { 
     long sleepRemaining; 
     long sleepStart = System.currentTimeMillis(); 
     sleepRemaining = CLEANUP_CYCLE_MS; 
     while (true) { 
      try { 
       sleepStart = System.currentTimeMillis(); 
       Thread.sleep(sleepRemaining); 
       cleanUp(); 
       sleepRemaining = CLEANUP_CYCLE_MS; 
      } catch (InterruptedException e) { 
       sleepRemaining = System.currentTimeMillis() - sleepStart; 
      } 
     } 
    } 
    private void cleanUp() { 
     Long currentTime = System.currentTimeMillis(); 
     synchronized(tokenMap) { 
      for (String user : tokenMap.keySet()) { 
       TimeStampedToken<User> tok = tokenMap.get(user); 
       if (tok.accessed + OBJECT_LIVE_TIME < currentTime) { 
        tokenMap.remove(user); 
       } 
      } 
     } 
    } 

} 
public void addToken(User tok) { 
    synchronized(tokenMap) { 
     tokenMap.put(tok.getUserId(), new TimeStampedToken<User>(tok)); 
    } 
} 
public User getToken(String userId) { 
    synchronized(tokenMap) { 
     TimeStampedToken<User> user = tokenMap.get(userId); 
     if (user != null) { 
      user.accessed = System.currentTimeMillis(); 
      return user.payload; 
     } else { 
      return null; 
     } 

    } 
} 
private static class TimeStampedToken<E> { 
    public TimeStampedToken(E payload) { 
     this.payload = payload; 
    } 
    public long accessed = System.currentTimeMillis(); 
    public E payload; 
} 
} 
+2

캐시 클래스의 기능과 정확히 무슨 문제입니까? 그것은 (기능) 잘못 작동합니까? 아니면 (POST가 너무 오래 걸리는) 원하는 속도보다 느리게 작동합니까? 그렇다면 어떤 메소드 호출에 너무 많은 시간이 소요됩니까? – ArjunShankar

+1

스레드를 구체적으로 중지하라는 메시지가 표시되는 경우에만 스레드를 다시 시작하는 이유는 무엇입니까? –

+0

만료 날짜가있는 캐시를 구현하려는 것 같습니다. 이것을 구현하는 훨씬 간단한 방법이 있거나, 이미 이것을하는 라이브러리를 사용할 수 있습니다. –

답변

1

다음은 어떻게 접근할까요? 멀티 스레드 코드를 사용하면 작동 가능성이 높아짐에 따라 단순성이 가장 좋은 방법 일 때가 많습니다.

...

public enum UserStore { 
    ; 

    interface User { 
     String getUserId(); 
    } 

    // a LRU cache with a timestamp. 
    private static final Map<String, TimeStampedToken<User>> tokenMap = new LinkedHashMap<String, TimeStampedToken<User>>(16, 0.7f, true); 
    private static final long OBJECT_LIVE_TIME = 299900; 

    public static synchronized void addToken(User tok) { 
     final long now = System.currentTimeMillis(); 
     // clean up as we go 
     for (Iterator<Map.Entry<String, TimeStampedToken<User>>> iter = tokenMap.entrySet().iterator(); iter.hasNext();) { 
      final Map.Entry<String, TimeStampedToken<User>> next = iter.next(); 
      if (next.getValue().accessed + OBJECT_LIVE_TIME >= now) 
       // the map is ordered by access time so there are no more to clean up. 
       break; 
      iter.remove(); 
     } 
     // add a new entry 
     tokenMap.put(tok.getUserId(), new TimeStampedToken<User>(tok, now)); 
    } 

    public static synchronized User getToken(String userId) { 
     final long now = System.currentTimeMillis(); 
     TimeStampedToken<User> user = tokenMap.get(userId); 
     if (user == null) 
      return null; 

     user.accessed = now; 
     return user.payload; 
    } 

    static class TimeStampedToken<E> { 
     long accessed; 
     final E payload; 

     TimeStampedToken(E payload, long now) { 
      this.payload = payload; 
      accessed = now; 
     } 
    } 
} 
0

이 라인은 나에게 이상한 보이는

sleepRemaining를 (세 번째 매개 변수의 LinkedHashMap의 true이지도를 통해 반복자 오히려 삽입 순서보다 액세스의 순서를 따라야한다는 것을 의미) = System.currentTimeMillis() - sleepStart;

... 확실히 그것이 있어야 ...

sleepRemaining = CLEANUP_CYCLE_MS - (에 System.currentTimeMillis() - sleepStart);

관련 문제