通过方法公开的Servlet静态字段 - 需要数据完整性

时间:2014-04-29 06:22:09

标签: java multithreading

我写了一个名为AbcServlet.java的Servlet,我有一个Static String field called cacheSeller。它正在servlet的init doGet方法中填充。它由CacheSellerClearThread.java清除,如下所示:

AbcServlet.java

public class AbcServlet extends HttpServlet {

    private static String cacheSeller = null;

    @Override
    public void init() throws ServletException {
        super.init();
        cacheSeller = populateCacheSeller();
    }

    /**
     * As soon as multiple requests are coming doGet is being called
     */
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        synchronized (this.getClass()) {
            if (cacheSeller == null) {
                cacheSeller = populateCacheSeller();
            }
        }

    }

    private String populateCacheSeller() {
        String fetchItFromSomewhere = "";// some logic to fetch the sting
        return fetchItFromSomewhere;
    }

    public static synchronized void clearCacheSeller() {
        cacheSeller = null;
    }
}

CacheSellerClearThread.java

/**
 * This thread is clearing the string field cacheSeller of AbcServlet 
 *
 */
public class CacheSellerClearThread extends Thread {

    public void run() {
        while (true) {
            try {
                Thread.sleep(2000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //Here it is clearing the static string field cacheSeller of AbcServlet
            AbcServlet.clearCacheSeller();
        }
    }
}

如果查看代码,则在doGet方法中读取和修改cacheSeller值。所有请求线程都将执行doGet,同时CacheSellerClearThread将在每2秒后清除其值。所以为了保持cacheSeller值的数据完整性,我使用同步。

我需要一个建议,即如果没有在clearCacheSeller方法中使用显式同步并且在Servlet中没有应用显式类级别锁定,是否有任何其他方法可以实现此目的。 (我的意思是说使用任何高级并发API,如原子引用或您可以建议的任何其他API。)

我已经使用AtomicReference编写了新的实现,请建议,我是否需要在任何地方使用同步。

public class AbcServlet extends HttpServlet {

    private static AtomicReference<String> cacheSeller = new AtomicReference<String>();

    @Override
    public void init() throws ServletException {
        super.init();
        cacheSeller.set(populateCacheSeller());
    }

    /**
     * As soon as multiple requests are coming doGet is being called
     */
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        if (cacheSeller.get() == null) {
            cacheSeller.compareAndSet(null, populateCacheSeller());
        }
        resp.getWriter().print(cacheSeller.get());
    }

    private String populateCacheSeller() {
        String fetchItFromSomewhere = "";// some logic to fetch the sting
        return fetchItFromSomewhere;
    }

    public static void clearCacheSeller() {
        cacheSeller.set(null);
    }
}

2 个答案:

答案 0 :(得分:2)

我建议使用Executors框架。

Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                AbcServlet.clearCacheSeller();
            }
        }, 2L,2L,TimeUnit.SECONDS);

在您的doGet中,仅同步初始化:

 @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String localCacheSeller = cacheSeller; 
            if (cacheSeller == null) {
                synchronized (this.getClass()) {
                     if (cacheSeller == null) { //need this because cacheSeller may already have been initialized while we waited
                         cacheSeller = populateCacheSeller();
                         localCacheSeller = cacheSeller; //because the executor may clear it after we initialized
                     }
                }
            }
    }

答案 1 :(得分:0)

您可以使用Google Guava之类的内容来创建缓存。

例如:

LoadingCache<String, String> cache =
 CacheBuilder.newBuilder()
 .expireAfterWrite(2, TimeUnit.MINUTES)
 .build(loader);

有了这样的东西,你可以避免你的辅助线程清除缓存。

这个实现是一个简单的内存缓存,它也是线程安全的。

此致