EJB Singleton是否已同步?

时间:2019-02-04 18:53:57

标签: singleton ejb linkedhashmap

我已经实现了一种缓存bean来将数据对象缓存为EJB Singleton。我想知道这在EJB中是否正确:

@Singleton
public class MyCache {

    int DEFAULT_CACHE_SIZE = 30;
    int DEFAULT_EXPIRES_TIME = 60000;
    long expiresTime = 0;
    long lastReset = 0;
    Cache cache = null; 

    ....
    @PostConstruct
    void init() {
        resetCache();
    }

    public void resetCache() {
        cache = new Cache(DEFAULT_CACHE_SIZE);
        lastReset = System.currentTimeMillis();
    }

    public void put(String key, Object value) {
        cache.put(key, value);
    }

    public Object get(String key) {
        // test if cache is expired
        if (expiresTime > 0) {
            Long now = System.currentTimeMillis();
            if ((now - lastReset) > expiresTime) {
                logger.finest("...... Cache expired!");
                resetCache();
            }
        }
        return cache.get(key);
    }



    class Cache extends LinkedHashMap<String, Object> implements Serializable {
        private static final long serialVersionUID = 1L;
        private final int capacity;

        public Cache(int capacity) {
            super(capacity + 1, 1.1f, true);
            this.capacity = capacity;
        }

        protected boolean removeEldestEntry(Entry<String, Object> eldest) {
            return size() > capacity;
        }
    }
}

我的问题是:这是实现应用程序范围缓存机制的正确方法吗?

我的印象是缓存的内容意外更改。这会发生吗?例如,如果EJB被钝化了? 我在Payara41服务器上运行。

或者我必须使用:

cache = Collections.synchronizedMap(new Cache(DEFAULT_CACHE_SIZE));

代替:

cache = new Cache(DEFAULT_CACHE_SIZE);

3 个答案:

答案 0 :(得分:1)

首先,由于未为您的bean指定并发管理,因此它归结为默认的“容器”。

根据EJB 3.1规范:

  

在设计Singleton会话Bean时,开发人员必须决定   Bean将使用容器托管还是Bean托管   并发。通常,将Singleton Bean指定为具有   容器管理的并发划分。如果没有,这是默认值   指定了并发管理类型。

然后,容器并发管理需要锁定类型的方法级规范。一旦不存在这些内容,就会应用默认的“写”:

  

默认情况下,如果没有并发锁定属性注释   为具有容器管理的Singleton bean的方法指定   并发划分,并发锁定的值   该方法的属性定义为Write。

以上内容意味着对bean方法的访问必须同步,甚至可能超出您的实际需要。您可以为只读方法(获取)设置“读取”锁定类型,以允许并发读取访问。

答案 1 :(得分:0)

采用容器管理锁的解决方案有一个缺点, 考虑一下您是否具有带有WRITE锁定的put操作,这意味着整个“缓存”都被阻止,因此无论密钥是否不同,都无法并行进行get或put操作。 如果您的缓存实现是并发Map,则可以使用它而无需锁定。

如果您对缓存有更多要求,我会使用Infinispan来提供更好的性能。这里的缓存可以是本地缓存,也可以分布在群集中。

答案 2 :(得分:0)

阅读此blog from Adam Bien之后,我现在通过以下方式改进了代码:

  • 我添加了注释ConcurrencyManagement
  • 我将LinkedHashMap更改为ConcurrentHashMap。

示例:

@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN) // added concurrency management
public class MyCache {

    int DEFAULT_CACHE_SIZE = 30;
    int DEFAULT_EXPIRES_TIME = 60000;
    long expiresTime = 0;
    long lastReset = 0;
    Cache cache = null; 

    ....
    @PostConstruct
    void init() {
        resetCache();
    }

    public void resetCache() {
        cache = new Cache(DEFAULT_CACHE_SIZE);
        lastReset = System.currentTimeMillis();
    }

    public void put(String key, Object value) {
        cache.put(key, value);
    }

    public Object get(String key) {
        // test if cache is expired
        if (expiresTime > 0) {
            Long now = System.currentTimeMillis();
            if ((now - lastReset) > expiresTime) {
                logger.finest("...... Cache expired!");
                resetCache();
            }
        }
        return cache.get(key);
    }

    // changed from LinkedHashMap to ConcurrentHashMap
    class Cache extends ConcurrentHashMap<String, Object> implements Serializable {
        private static final long serialVersionUID = 1L;
        private final int capacity;

        public Cache(int capacity) {
            super(capacity + 1, 1.1f);
            this.capacity = capacity;
        }

        protected boolean removeEldestEntry(Entry<String, Object> eldest) {
            return size() > capacity;
        }
    }
}

我认为这是更正确的实现方式。