这段线程代码中的错误在哪里?

时间:2011-12-21 11:12:21

标签: java thread-safety

我创建了一个用作缓存提供程序的类。它使用Map,带时间戳的映射条目,它产生一个每隔一段时间执行清理的线程。此类用于Web应用程序。这个Web应用程序有一个问题,POST将需要30秒。我将问题追溯到此缓存类,消除它可以解决问题。

我尽力在本课程中找到错误,但我不能。请帮帮我。 假设User类是某种描述用户的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 个答案:

答案 0 :(得分:1)

以下是我将如何处理它。对于多线程代码,简单性通常是最好的方法,因为它更有可能工作。

(LinkedHashMap的第三个参数true表示此Map上的迭代器遵循访问顺序而不是插入顺序)

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;
        }
    }
}

答案 1 :(得分:0)

这条线对我来说很奇怪......

sleepRemaining = System.currentTimeMillis() - sleepStart;

......当然应该是......

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