代码之家  ›  专栏  ›  技术社区  ›  Ralph

EJB单例是否同步?

  •  0
  • Ralph  · 技术社区  · 6 年前

    我实现了一种缓存bean,将数据对象缓存为EJB单例。我想知道这在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 回复  |  直到 6 年前
        1
  •  1
  •   user3714601    6 年前

    首先,由于没有为您的bean指定并发管理,它可以归结为默认的“容器”。

    来自EJB3.1规范:

    在设计单例会话bean时,开发人员必须决定 同意。通常,单例bean将被指定为 已指定并发管理类型。

    然后,容器并发管理需要锁类型的方法级规范。一旦不存在,则默认的“写入”适用于:

    默认情况下,如果并发锁定属性注释不是 为具有容器管理的单例bean的方法指定 方法的属性被定义为Write。

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

        2
  •  0
  •   wfink    6 年前

    考虑一下,如果有一个带有写锁的put操作,则意味着整个“缓存”被阻塞,因此无论密钥是否不同,都不可能并行执行get或put操作。 如果您的缓存实现是一个并发映射,则可以在不锁定的情况下使用它。

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

        3
  •  0
  •   Ralph    6 年前

    读完这个 blog from Adam Bien 我现在通过以下方式改进了代码:

    • 我添加了注释并发管理

    例子:

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

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

    推荐文章