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

标签 java thread-safety

我创建了一个用作缓存提供程序的类。它使用一个映射、带时间戳的映射条目,并生成一个经常执行清理的线程。此类在 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; 
}
}

最佳答案

这是我的处理方法。对于多线程代码,简单性通常是最好的方法,因为它更有效。

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

关于java - 这段线程代码的错误在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8588889/

相关文章:

c# - C# 的 using 语句中止安全吗?

objective-c - 后台线程中可取消的加载

java - 如何使用java程序将文本文件压缩为rar格式

java - JSoup 抓取不断变化的值(value)

java - 尽管有 'DB_CLOSE_ON_EXIT=FALSE',但 H2 内存中测试数据库已关闭

c++ - 为什么只有一个锁和一个原子计数器的条件变量会错误地唤醒?

c# - 线程和网络浏览器控制

java - Spring Java 项目在导入具有无限循环的 jar 时未启动 Tomcat

java - Spring & Maven & JUnit - BeanCreationException : Could not autowire field NoSuchBeanDefinitionException: No qualifying bean of type

java - 使用构建器/工厂模式确保内存可见性