背景:不久前單位上線一款應用,上了生產環境之后,沒過多久,便吃掉了服務器所有的內存,最后導致網站服務掛了。
在解決了這一問題之后,我發現這其實是典型的一單例模式,現分享一下。
之前存在問題的老代碼如下:
這是導致問題所在的那個關鍵方法
public synchronized static JedisCluster getJedisCluster() { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(MAX_ACTIVE); config.setMaxIdle(MAX_IDLE); config.setMaxWaitMillis(MAX_WAIT); config.setTestOnBorrow(TEST_ON_BORROW); // 集群模式 JedisPoolConfig poolConfig = new JedisPoolConfig(); Set<HostAndPort> nodes = new HashSet<HostAndPort>(); HostAndPort hostAndPort1 = new HostAndPort("服務器地址1", 端口1); HostAndPort hostAndPort2 = new HostAndPort("服務器地址2", 端口2); HostAndPort hostAndPort3 = new HostAndPort("服務器地址3", 端口3); nodes.add(hostAndPort1); nodes.add(hostAndPort2); nodes.add(hostAndPort3); JedisCluster jedisCluster = new JedisCluster(nodes, poolConfig); return jedisCluster; }
以上這段代碼是有問題的,大家看出來了嗎?
問題在於,雖然方法聲明為synchronized static,但是在並發多線程的情況下,並不能保證每個用戶線程只生成一個JedisCluster的實例。
這樣就會導致每個線程都會創建jedisCluster的實例,就會消耗內存,而且這塊內存又沒有被及時地釋放掉,導致多用戶並發以后,快速吃光了服務器的內存。
解決方法就是使用單例模式,把JedisCluster作為static的類成員,且使用懶漢單例模式,代碼如下:
public class OuterClass{ ... private static JedisCluster jedisCluster = null; ... public synchronized static JedisCluster getJedisCluster() { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(MAX_ACTIVE); config.setMaxIdle(MAX_IDLE); config.setMaxWaitMillis(MAX_WAIT); config.setTestOnBorrow(TEST_ON_BORROW); // 集群模式 JedisPoolConfig poolConfig = new JedisPoolConfig(); Set<HostAndPort> nodes = new HashSet<HostAndPort>(); HostAndPort hostAndPort1 = new HostAndPort("服務器地址1", 端口1); HostAndPort hostAndPort2 = new HostAndPort("服務器地址2", 端口2); HostAndPort hostAndPort3 = new HostAndPort("服務器地址3", 端口3); nodes.add(hostAndPort1); nodes.add(hostAndPort2); nodes.add(hostAndPort3); // 只有當jedisCluster為空時才實例化 if (jedisCluster == null) { jedisCluster = new JedisCluster(nodes, poolConfig); } return jedisCluster; } }
這樣就會保證即使在高並發的環境下,所有用戶線程還是只會擁有一個JedisCluster的實例。