記一個由於代碼編碼問題導致的死鎖


  項目中使用了redis,由於項目更迭,配置也比較亂,在1點幾的時候用的配置參數是maxActive等,在redis的2點幾版本中其實已經廢棄了,也沒有仔細研究就直接去掉了,導致redis池中的可用資源一直是默認的8個,而且還有個更大的問題,配置的超時時間不對。見下圖所示,配置在JedisPool的初始化參數中,這個沒仔細看代碼,瞄了一眼應該是不管用的。

  真正的配置應該在這里:

  JedisPoolConfig中maxTotal也應該根據項目需要選擇合適的大小,否則只會是默認的8個!!

  回歸正題,遇到的問題現象就是系統失去響應,查看線程dump發現所有的線程都卡在這:

"DubboServerHandler-192.168.213.250:20889-thread-198" daemon prio=10 tid=0x00007f3a3b794800 nid=0x83b waiting on condition [0x00007f3a1ed86000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x0000000700c6c208> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
    at org.apache.commons.pool2.impl.LinkedBlockingDeque.takeFirst(LinkedBlockingDeque.java:583)
    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:442)
    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)
    at redis.clients.util.Pool.getResource(Pool.java:48)
    at redis.clients.jedis.JedisPool.getResource(JedisPool.java:86)

  所有的線程都是處於WAITING狀態,注意不是TIME_WAITING,也就是說如果沒有別的線程去喚醒他們根本就會是一直在WAITING,關鍵所有的線程都在WAITING,誰去喚醒啊啊啊啊啊!!!

  看現象就是在等待獲取資源,看jedis的源碼也是池中沒有資源了卡在那里。折騰了一上午,終於找到可能的原因,在一個方法中先去jedisPool中取了資源,在沒有關閉的前提下,然后調用了另一個方法,在第二個方法中又去jedisPool中獲取了資源,結合前面jedisPool中初始化只有8個資源,如果同時超過8個請求到達貌似就會造成這個問題。寫代碼驗證,的確如此。貼下現象重現的源碼:

public class RedisTest {
    public static void main(String[] args) {
        JedisPoolConfig jpc = new JedisPoolConfig();
        jpc.setMaxIdle(20);
        jpc.setMaxTotal(20);
        //jpc.setMaxWaitMillis(10000);
        final JedisPool jedisPool = new JedisPool(jpc, "192.168.213.250", 6379);

        final CountDownLatch latch = new CountDownLatch(100);
        for (int i = 0; i < 100; i++) {
            final int counter = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(counter + ":ready!!!");
                    try {
                        latch.await();
                        System.out.println(counter + ":" + getInfo(jedisPool));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            latch.countDown();
        }
    }

    public static String getInfo(JedisPool jedisPool) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            setInfo(jedisPool);
            String key = "key123456";
            return jedis.get(key);
        } finally {
             jedis.close();
        }
    }
    public static String setInfo(JedisPool jedisPool) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String key = "key123456";
            return jedis.set(key,"value123456");
        } finally {
             jedis.close();
        }
    }
    
}

  就是這么死鎖啦,如果設置了MaxWaitMillis還好,否則只能重啟JVM了。

  還有切記在用完jedis的時候一定要顯式的調用下close方法,否則資源不會回到池中,很快就會用完啦,然后你就又要重啟JVM了。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM