2009-07-01 11 views
5

निम्न वर्ग चलाते समय निष्पादन सेवा अक्सर डेडलॉक होगी।हैश मैप ऑपरेशंस करते समय निष्पादक सेवा डेडलॉक क्यों करता है?

import java.util.ArrayList; 
import java.util.Collection; 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 


public class ExecutorTest { 
public static void main(final String[] args) throws InterruptedException { 
    final ExecutorService executor = Executors.newFixedThreadPool(10); 

    final HashMap<Object, Object> map = new HashMap<Object, Object>(); 
    final Collection<Callable<Object>> actions = new ArrayList<Callable<Object>>(); 
    int i = 0; 
    while (i++ < 1000) { 
     final Object o = new Object(); 
     actions.add(new Callable<Object>() { 
      public Object call() throws Exception { 
       map.put(o, o); 
       return null; 
      } 
     }); 
     actions.add(new Callable<Object>() { 
      public Object call() throws Exception { 
       map.put(new Object(), o); 
       return null; 
      } 
     }); 
     actions.add(new Callable<Object>() { 
      public Object call() throws Exception { 
       for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) { 
        iterator.next(); 
       } 
       return null; 
      } 
     }); 
    } 
    executor.invokeAll(actions); 
    System.exit(0); 
} 

} 

तो ऐसा क्यों होता है? या बेहतर अभी तक - मैं यह सुनिश्चित करने के लिए एक परीक्षण कैसे लिख सकता हूं कि एक कस्टम अमूर्त मानचित्र के कार्यान्वयन थ्रेड सुरक्षित हैं? (कुछ कार्यान्वयन में कई मानचित्र हैं, एक कैश कार्यान्वयन आदि के लिए अन्य प्रतिनिधि)

कुछ पृष्ठभूमि: यह विंडोज 1.6.0_04 और 1.6.0_07 के तहत विंडोज पर होता है। मुझे पता है कि समस्या sun.misc.Unsafe.park (से आता है):

  • मैं अपने Core2 जोड़ी 2.4Ghz लैपटॉप पर समस्या को पुन: कर सकते हैं लेकिन डिबग में नहीं चल रहा है, जबकि
  • मैं अपने Core2 पर डीबग कर सकते हैं काम पर Quad, लेकिन मैं इसे आरडीपी पर लटका दिया है, इसलिए नहीं

अधिकांश जवाब नीचे कल तक एक स्टैक ट्रेस मिल HashMap के गैर धागा सुरक्षा के बारे में कर रहे हैं करने के लिए सक्षम हो जाएगा, लेकिन मैं मिल सकता है हैश मैप में कोई लॉक थ्रेड नहीं - यह सब निष्पादन सेवा कोड (और Unsafe.park()) में था। मैं कल धागे की बारीकी से जांच करूँगा।

यह सब क्योंकि एक कस्टम सार नक्शा कार्यान्वयन थ्रेड-सुरक्षित नहीं था इसलिए मैंने यह सुनिश्चित करने के लिए सेट किया कि सभी कार्यान्वयन थ्रेड-सुरक्षित होंगे। संक्षेप में, मैं यह सुनिश्चित करना चाहता हूं कि ConcurrentHashMap आदि की मेरी समझ ठीक वही है जो मुझे उम्मीद है, लेकिन निष्पादन सेवा को अजीब कमी के कारण मिल गया है ...

+0

कल के लिए मेरे वर्तमान कार्रवाई: सूत्र के लिए 1. जांच बारीकी से HashMap में बंद कर दिया [आकार बदलने] कोड 2. अपग्रेड नवीनतम वीएम – Stephen

+0

लिए आप किसी भी धागे का आकार बदलने में बंद कर दिया नहीं मिलेगा - वे का आकार बदलने और बाहर निकलने होगा। कॉल करने योग्य फिर से चलने वाले थ्रेड के लिए जांचें। –

उत्तर

16

आप एक प्रसिद्ध ज्ञात-थ्रेड-सुरक्षित का उपयोग कर रहे हैं वर्ग और डेडलॉक के बारे में शिकायत। मैं यह देखने में असफल रहा कि समस्या क्या है।

इसके अलावा, कैसे ExecutionService

strangely lacking 

है?

यह एक आम गलतफहमी है कि का उपयोग करके उदा। एक HashMap आपको कुछ पुराने डेटा मिलेंगे। a beautiful race condition देखें कि आप बस ऐसा करके अपने JVM को कैसे उड़ा सकते हैं।

यह समझना क्यों होता है यह एक बहुत मुश्किल प्रक्रिया है और दोनों JVM और कक्षा पुस्तकालयों के आंतरिक के ज्ञान की आवश्यकता है।

ConcurrentHashMap के लिए, बस javadoc पढ़ें - इसे आपके प्रश्नों को स्पष्ट करना चाहिए। यदि नहीं, तो Java Concurrency in Practice पर एक नज़र डालें।


अद्यतन:

मैं अपनी स्थिति पुन: पेश करने में कामयाब रहे, लेकिन यह एक गतिरोध नहीं है। actions में से कोई भी निष्पादन पूरा नहीं करता है। स्टैक ट्रेस है:

"pool-1-thread-3" prio=10 tid=0x08110000 nid=0x22f8 runnable [0x805b0000] 
java.lang.Thread.State: RUNNABLE 
at ExecutorTest$3.call(ExecutorTest.java:36) 
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) 
at java.util.concurrent.FutureTask.run(FutureTask.java:138) 
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) 
at java.lang.Thread.run(Thread.java:619) 

यह सटीक मामले मैं से जुड़ा हुआ की तरह दिखता है - HashMap आकार दिया जाता है और इटरेटर का आकार बदलने के आंतरिक यांत्रिकी के कारण एक अनंत लूप में अटक जाता है।

जब ऐसा होता है, invokeAll कभी वापस नहीं आता है और प्रोग्राम लटकता है।लेकिन यह न तो एक डेडलॉक है, न ही एक livelock, लेकिन दौड़ की स्थिति

+0

मुझे वह मिलता है (मुझे लगता है - मैं दौड़ की स्थिति के बारे में पढ़ूंगा)। मुद्दा यह है कि मुझे हैश मैप कोड में वास्तव में लॉक किए गए कोई धागे नहीं मिला। यह सब निष्पादन सेवा क्यूइंग कार्यक्षमता में था। जैसे Unsafe.park() जैसा कि कहा गया है। – Stephen

+0

मुझे 6u14 का उपयोग करके मेरे कोर 2 पर डेडलॉक्स नहीं मिलते हैं इसलिए मैं इसके बारे में कुछ भी नहीं कह सकता। आप यह देखने के लिए कि क्या डेडलॉक बनी हुई है, आप केवल ConcurrentHashMap का उपयोग करने का प्रयास करना चाहेंगे। लेकिन मुझे यहां डेडलॉक के लिए कोई कारण नहीं दिख रहा है। –

+0

@ स्टीफन - मैंने इसे पुन: उत्पन्न करने में कामयाब रहा और स्पष्टीकरण जोड़ा है। –

2

आप डेडलॉक से क्या समझते हैं?

कोड के साथ कम से कम दो समस्याएं हैं। HashMap एक साथ कई धागे से प्रयोग किया जाता है और इसलिए एक अनंत लूप में जा सकता है। अंतर्निहित डेटा संरचना को संभावित रूप से बदलते समय आप एंट्री सेट को फिर से सक्रिय कर रहे हैं (भले ही प्रत्येक व्यक्तिगत ऑपरेशन सिंक्रनाइज़ किया गया हो hasNext/next परमाणु नहीं होगा)।

यह भी ध्यान रखें कि नवीनतम सिंहाइनाइज्ड सुरक्षा रिलीज (एसएसआर) के साथ 1.6.0 अद्यतित संस्करण 1.6.0_13 और 1.6.0_14 हैं।

+0

क्या आप एसएसआर रिलीज में क्या शामिल है इसके बारे में विवरण प्रदान कर सकते हैं? – pjp

+0

@pjp अब वे विशेष सीपीयू (क्रिटिकल पैच अपडेट्स, विशेष के रूप में जानते हैं क्योंकि वे वर्तमान में ओरेकल क्वाटरली सीपीयू के साथ सिंक्रनाइज़ नहीं हैं)। लिखने के समय जोखिम मैट्रिक्स के साथ नवीनतम सलाहकार का एक लिंक यहां दिया गया है: http://www.oracle.com/technetwork/topics/security/javacpujune2011-313339.html#AppendixJAVA कोई और विवरण आमतौर पर नहीं दिया जाता है। –

0

परीक्षण काम करने के संदर्भ में - के बजाय:

executor.invokeAll(actions); 

उपयोग

executor.invokeAll(actions, 2, TimeUnit.SECONDS); 

भी ध्यान दें कि परीक्षण वास्तव में काम (और त्रुटियों की रिपोर्ट) बनाने के लिए, आप की आवश्यकता होगी कुछ ऐसा करें:

List<Future> results = executor.invokeAll(actions, 2, TimeUnit.SECONDS); 
executor.shutdown(); 
for (Future result : results) { 
    result.get(); // This will report the exceptions encountered when executing the action ... the ConcurrentModificationException I wanted in this case (or CancellationException in the case of a time out) 
} 
//If we get here, the test is successful... 
1

मेरा मानना ​​है कि आपके मानचित्र को समवर्ती रूप से संशोधित किया जा रहा है। यदि डाल दिया जाता है() को तब कहा जाता है जब आपकी पुनरावृत्ति कार्रवाई प्रगति पर होती है, कुछ स्थितियों के तहत (विशेष रूप से यदि आकार बदलना होता है) तो आप अनंत लूप में समाप्त हो सकते हैं। यह एक काफी प्रसिद्ध व्यवहार है (here देखें)।

डेडलॉक और एक अनंत लूप स्वयं को बहुत अलग दिखाएगा। यदि आपके पास एक असली डेडलॉक है, तो थ्रेड डंप स्पष्ट रूप से इंटरलॉकिंग थ्रेड दिखाएगा। दूसरी तरफ, एक बार जब आप अनंत लूप में आ जाएंगे, तो आपका सीपीयू उच्च गति से बढ़ेगा और जब भी आप डंप लेते हैं तो स्टैक निशान अलग-अलग होंगे।

इसमें हैश मैप के असुरक्षित समवर्ती उपयोग के साथ निष्पादक और सब कुछ करने के लिए इसका कोई लेना-देना नहीं है जिसे कभी भी इस तरह इस्तेमाल करने के लिए डिज़ाइन नहीं किया गया था। असल में धागे के मुट्ठी भर के साथ इस समस्या को पुन: पेश करना बहुत आसान है।

इसका सबसे अच्छा समाधान ConcurrentHashMap पर स्विच करना है। यदि आप एक सिंक्रनाइज़ हैश मैप या हैशटेबल पर स्विच करते हैं, तो आप अनंत लूप में नहीं पहुंचेंगे, लेकिन फिर भी आप पुनरावृत्ति के दौरान ConcurrentModificationExceptions प्राप्त कर सकते हैं।

संबंधित मुद्दे