2012-03-28 29 views
9

मैं JDK6 साथ ThreadPoolExecutor का उपयोग कर धागा पूलिंग के लिए विभिन्न रणनीतियों के साथ चारों ओर खिलवाड़ किया गया है। मेरे पास प्राथमिकता कतार काम कर रही है लेकिन मुझे यकीन नहीं था कि अगर मुझे एलीवटाइम (जो आपको एक असंबद्ध कतार के साथ मिलता है) के बाद पूल का आकार नहीं था। तो, मैं एक LinkedBlockingQueue और CallerRuns नीति का उपयोग कर ThreadPoolExecutor पर देख रहा हूं।KeepAliveTime के बाद ThreadPoolExecutor CorePoolSize के नीचे धागे को कम क्यों करता है?

मेरे साथ अब समस्या यह है कि पूल रैंप ऊपर है, क्योंकि दस्तावेज़ों को यह समझाया जाता है कि यह चाहिए, लेकिन कार्यों के पूरा होने के बाद और KeepAliveTime खेलने में आता है PoolSize पूल को शून्य से कम कर देता है। नीचे दिए गए उदाहरण कोड आप मेरे सवाल के लिए आधार देखते हैं चाहिए:

public class ThreadPoolingDemo { 
    private final static Logger LOGGER = 
     Logger.getLogger(ThreadPoolingDemo.class.getName()); 

    public static void main(String[] args) throws Exception { 
     LOGGER.info("MAIN THREAD:starting"); 
     runCallerTestPlain(); 
    } 

    private static void runCallerTestPlain() throws InterruptedException { 
     //10 core threads, 
     //50 max pool size, 
     //100 tasks in queue, 
     //at max pool and full queue - caller runs task 
     ThreadPoolExecutor tpe = new ThreadPoolExecutor(10, 50, 
      5L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(100), 
      new ThreadPoolExecutor.CallerRunsPolicy()); 

     //dump 5000 tasks on the queue 
     for (int i = 0; i < 5000; i++) { 
      tpe.submit(new Runnable() { 
       @Override 
       public void run() { 
        //just to eat some time and give a little feedback 
        for (int j = 0; j < 20; j++) { 
         LOGGER.info("First-batch Task, looping:" + j + "[" 
           + Thread.currentThread().getId() + "]"); 
        } 
       } 
      }, null); 
     } 
     LOGGER.info("MAIN THREAD:!!Done queueing!!"); 

     //check tpe statistics forever 
     while (true) { 
      LOGGER.info("Active count: " + tpe.getActiveCount() + " Pool size: " 
       + tpe.getPoolSize() + " Largest Pool: " + tpe.getLargestPoolSize()); 
      Thread.sleep(1000); 
     } 
    } 
} 

मैं एक पुराने बग कि इस मुद्दे को हो रहा है पाया, लेकिन यह बंद हो गया: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6458662। क्या यह अभी भी 1.6 में मौजूद हो सकता है या क्या मुझे कुछ याद आ रहा है?

ऐसा लगता है कि मैं रबड़ की तरह इस एक (http://www.codinghorror.com/blog/2012/03/rubber-duck-problem-solving.html) ducked। ऊपर से जुड़ी बग इस से संबंधित है: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6576792, जहां समस्या 1.7 में हल हो रही है (मैंने 1.7 लोड किया और सत्यापित - तय ...)। मुझे लगता है कि मेरी मुख्य समस्या यह थी कि इस मूलभूत बग लगभग एक दशक तक बना रहा। मैंने इसे पोस्ट करने के लिए इसे लिखने में बहुत अधिक समय बिताया, उम्मीद है कि यह किसी की मदद करेगी।

+0

+1 अच्छा खोज, इस व्यवहार को देखने के लिए खुद को हैरान था। –

+1

शायद आपकी पोस्ट को एक प्रश्न के रूप में व्यवस्थित करना बेहतर होगा और फिर उत्तर के रूप में आपने जो सीखा है उसे प्रदान करें? –

उत्तर

6

... कार्यों को पूरा करने के बाद और KeepAliveTime खेलने में आता है PoolSize पूल को शून्य से कम करने से पता चलता है।

तो यह ThreadPoolExecutor में दौड़ की स्थिति दिखता है। मुझे लगता है कि यह उम्मीद के बावजूद डिजाइन के अनुसार काम कर रहा है। getTask() तरीका है जिसके अवरुद्ध कतार से कार्य प्राप्त करने के लिए के माध्यम से कार्यकर्ता धागे पाश, तो आप इस कोड को देखने में:

if (state == SHUTDOWN) // Help drain queue 
    r = workQueue.poll(); 
else if (poolSize > corePoolSize || allowCoreThreadTimeOut) 
    r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS); 
else 
    r = workQueue.take(); 
if (r != null) 
    return r; 
if (workerCanExit()) { 
    if (runState >= SHUTDOWN) // Wake up others 
     interruptIdleWorkers(); 
    return null; 
} 

poolSizecorePoolSize तो ऊपर बढ़ता है बाहर keepAliveTime के बाद चुनाव के समय, कोड नीचे गिर जाता है, तो workerCanExit() को r के बाद से null है। एक बार जब कि रिटर्न true तो कार्यकर्ता धागा बाहर निकलता है और तोpoolSize कम कर रहा है

mainLock.lock(); 
    boolean canExit; 
    try { 
     canExit = runState >= STOP || 
      workQueue.isEmpty() || 
      (allowCoreThreadTimeOut && 
      poolSize > Math.max(1, corePoolSize)); << test poolSize here 
    } finally { 
     mainLock.unlock();       << race to workerDone() begins 
    } 

: के बाद से यह सिर्फ poolSize के राज्य परीक्षण कर रहा है धागे की सभी उस विधि से true लौट सकते हैं। कार्यकर्ता धागे के सभी एक ही समय में है कि परीक्षण करते हैं तो वे poolSize के परीक्षण और कार्यकर्ता जब --poolSize होता है की रोक के बीच दौड़ की वजह से सब बाहर हो जाएगा।

मुझे क्या आश्चर्य है कि कैसे संगत है कि रेस स्थिति है। आप नीचे दिए गए run() के अंदर sleep() करने के लिए कुछ randomization जोड़ने, तो आप प्राप्त कर सकते हैं कुछ कोर धागे बाहर नहीं करने के लिए, लेकिन मैं सोचा होगा रेस स्थिति हिट करने के लिए कठिन हो गया होता।


आप नीचे दिए गए परीक्षण में इस व्यवहार को देख सकते हैं:

private final Random random = new Random(); 
... 
    Thread.sleep(100 + random.nextInt(100)); 

यह कर देगा:

@Test 
public void test() throws Exception { 
    int before = Thread.activeCount(); 
    int core = 10; 
    int max = 50; 
    int queueSize = 100; 
    ThreadPoolExecutor tpe = 
      new ThreadPoolExecutor(core, max, 1L, TimeUnit.SECONDS, 
        new LinkedBlockingQueue<Runnable>(queueSize), 
        new ThreadPoolExecutor.CallerRunsPolicy()); 
    tpe.allowCoreThreadTimeOut(false); 
    assertEquals(0, tpe.getActiveCount()); 
    // if we start 1 more than can go into core or queue, poolSize goes to 0 
    int startN = core + queueSize + 1; 
    // if we only start jobs the core can take care of, then it won't go to 0 
    // int startN = core + queueSize; 
    for (int i = 0; i < startN; i++) { 
     tpe.submit(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        Thread.sleep(100); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
     }); 
    } 
    while (true) { 
     System.out.println("active = " + tpe.getActiveCount() + ", poolSize = " + tpe.getPoolSize() 
       + ", largest = " + tpe.getLargestPoolSize() + ", threads = " + (Thread.activeCount() - before)); 
     Thread.sleep(1000); 
    } 
} 

आप कुछ इस तरह के run() विधि के अंदर sleep लाइन को बदलते हैं दौड़ की स्थिति को हिट करने के लिए कठिन कुछ कोर धागे अभी भी आसपास होंगे।

+0

ओपी 'getPoolSize()' में अधिक दिलचस्पी है और 'getActiveCount() 'सभी कार्यों को पूरा करने के बाद और keepAliveTime समाप्त हो जाता है सभी धागे वास्तव में समाप्त हो जाते हैं हालांकि टीपीई नहीं करता है। –

+0

@ ग्रे मेरे टीपीई पैरामीटर महत्वपूर्ण हैं। यह हमेशा असफल नहीं होता है। आपके द्वारा उपयोग किए गए पैरामीटर का उपयोग करके हम इसे अपेक्षित रूप से काम करेंगे। मेरे द्वारा निर्दिष्ट पैराम एक अद्वितीय लेकिन यथार्थवादी तरीके से टीपीई के नियमों के साथ खेलते हैं (यानी कतार पर भारी बैच नौकरी डाली जा रही है)। आपके मानकों के साथ मुख्य समस्या यह है कि आप ऐसी परिस्थिति को कभी भी ट्रिगर नहीं करते हैं जो कोर आकार के ऊपर और उससे ऊपर के पूल में धागे को जोड़ता है। कोर आकार से अधिक/जोड़ने के लिए पूल कोर आकार (10) पर होना चाहिए और कतार इसकी सीमा (100) से अधिक होनी चाहिए। 'उदाहरण के लिए (int i = 0; i andematt

+0

@andematt दिलचस्प यात्रा। यह 'ThreadPoolExecutor' में दौड़ की स्थिति है। मैंने अपना जवाब संपादित कर लिया है। – Gray