2012-09-18 16 views
6

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

मुझे इसे थ्रेड-सुरक्षित तरीके से करने की आवश्यकता है।

यहां एक कोड लिखा है जो मैंने लिखा है। क्या यह धागा सुरक्षित है?

public class Test { 

    private static Map<Integer, Object> clientTypesInitiated = new ConcurrentHashMap<Integer, Object>(); 

    /* to process client request we need to 
    create corresponding client type data. 
    on the first signal we create that data, 
    on the second - we process the request*/ 

    void onClientRequestReceived(int clientTypeIndex) { 
     if (clientTypesInitiated.put(clientTypeIndex, "") == null) { 
      //new client type index arrived, this type was never processed 
      //process data for that client type and put it into the map of types 
      Object clientTypeData = createClientTypeData(clientTypeIndex); 
      clientTypesInitiated.put(clientTypeIndex, clientTypeData); 
     } else { 
      //already existing index - we already have results and we can use them 
      processClientUsingClientTypeData(clientTypesInitiated.get(clientTypeIndex)); 
     } 
    } 

    Object createClientTypeData(int clientIndex) {return new Object();} 

    void processClientUsingClientTypeData(Object clientTypeData) {} 
} 

एक हाथ से, ConcurrentHashMap map.put (ए, बी) == ही ए के लिए अशक्त दो बार दूसरी ओर से, काम और comparisson आपरेशन थ्रेड-सुरक्षित नहीं है का उत्पादन नहीं कर सकते हैं।

तो क्या यह कोड ठीक है? यदि नहीं, तो मैं इसे कैसे ठीक कर सकता हूं?

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

उत्तर

2

इस कोड थ्रेड नहीं है सुरक्षित है क्योंकि

//already existing index - we already have results and we can use them 
processClientUsingClientTypeData(clientTypesInitiated.get(clientTypeIndex)); 

"मूल्य" आप अस्थायी रूप से डाल जाँच में सम्मिलित होने का एक मौका है।

इस कोड thusly threadsafe बनाया जा सकता है:

public class Test { 

    private static Map<Integer, Object> clientTypesInitiated = new ConcurrentHashMap<Integer, Object>(); 

    /* to process client request we need to 
     create corresponding client type data. 
     on the first signal we create that data, 
     on the second - we process the request*/ 

void onClientRequestReceived(int clientTypeIndex) { 
    Object clientTypeData = clientTypesInitiated.get(clientTypeIndex); 
    if (clientTypeData == null) { 
     synchronized (clientTypesInitiated) { 
      clientTypeData = clientTypesInitiated.get(clientTypeIndex); 
      if (clientTypeData == null) { 
       //new client type index arrived, this type was never processed 
       //process data for that client type and put it into the map of types 
       clientTypeData = createClientTypeData(clientTypeIndex); 
       clientTypesInitiated.put(clientTypeIndex, clientTypeData); 
      } 
     } 
    } 
    processClientUsingClientTypeData(clientTypeData); 
} 

Object createClientTypeData(int clientIndex) {return new Object();} 

void processClientUsingClientTypeData(Object clientTypeData) {} 

}

+0

मुझे लगता है कि आपको 'processClientUsingClientTypeData' से पहले 'else' की आवश्यकता है। – Gray

+0

यह थ्रेड-सुरक्षित भी नहीं है! देखो: एक धागा मिल कार्यान्वित करता है, अशक्त के खिलाफ तुलना, चरणों में है, तो एक और धागा एक ही होता है, तो एक clientTypeData को प्रदान करती है और अंत में विधि से देता है, लेकिन एक और धागा clientTypeData ओवरराइड करता है। –

+0

@ krzysztof असुरक्षित क्या है? जैसे मैंने कहा, _if_ आप कभी-कभी CreateClientTypeData को एक से अधिक बार निष्पादित करने पर ध्यान नहीं देते हैं, यह ठीक है। यदि आप वर्णन करते हैं, तो दूसरा धागा परिणाम को पहले स्थान से बदल देगा। संभवतः चूंकि कोई भी अनुरोध प्रारंभिकरण को ट्रिगर कर सकता है, तो यह हर बार एक ही परिणाम देगा। यदि प्रारंभिकता _really_ महंगा है, तो उससे बचने के लिए और अधिक जटिलता जोड़ा जा सकता है। –

3

नहीं, मुझे नहीं लगता कि यह अभी भी थ्रेड-सुरक्षित है।

आपको एक सिंक्रनाइज़ ब्लॉक में ऑपरेशन को लपेटने की आवश्यकता है।

प्रति (प्राप्त सहित) आम तौर पर ब्लॉक नहीं करती javadoc for ConcurrentHashMap

रिट्रीवल संचालन के रूप में, तो अद्यतन संचालन के साथ ओवरलैप हो सकता है (डाल दिया और हटाने सहित)।

+0

यह ConcurrentMap के लिए सच नहीं है, वह अभी भी यह थ्रेड-सुरक्षित है और किसी भी तुल्यकालन, जो ConcurrentMap अप्रचलित बनाता है बिना हो सकता है। – jdevelop

+0

@jdevelop: क्या आप विस्तृत कर सकते हैं? – kosa

+0

वह ConcurrentMap – jdevelop

0

नहीं ऐसा नहीं है, आप पूरे विधि की जरूरत है सुरक्षित थ्रेड किए जाने की यह घोषणा के रूप में सिंक्रनाइज़, या एक सिंक्रनाइज़ ब्लॉक का उपयोग अपने महत्वपूर्ण कोड डालने के लिए।

+0

क्यों? आपको सूचना प्रदान करने की आवश्यकता है न केवल सरल प्रश्न का जवाब दें। आदेश सुरक्षित थ्रेड किए जाने में – Gray

+0

, बुनियादी जानकारी के लिए, गूगल अपने सबसे अच्छे दोस्त http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html –

+0

तुम मेरी बात को अनदेखा कर रहे हैं। _मुझे उत्तर पता है। SO उत्तर उत्तरदायित्व के लिए हैं और Google _destinations_ होने के लिए लोगों को Google को इंगित करने के लिए नहीं हैं। आपके उत्तरों को तब तक वोट नहीं दिया जाएगा जब तक कि वे अधिक जानकारी प्रदान न करें। – Gray

2

आपको यहां putIfAbsent का उपयोग करना चाहिए, इस ऑपरेशन के अर्थशास्त्र सीएएस के समान है, और यह निश्चित रूप से परमाणु है। और चूंकि यह परमाणु है - तो आपको आंतरिक असाइनमेंट और तुलना के साथ कोई समस्या नहीं है।

वर्तमान कोड थ्रेड-सुरक्षित नहीं है।

+0

अगर कुछ भी है तो जांच करने से पहले इफैब्सेंट को डेटा बनाने की आवश्यकता नहीं होगी? अधिकतर वहां होंगे, इसलिए इसमें कई अनावश्यक प्रारंभिक शामिल हैं। –

+0

अच्छा विचार, लेकिन वास्तव में, जैसा कि क्रिज़्ज़टॉफ ने कहा था, मुझे इस मामले में कई बार इनिट प्रक्रिया चलाना होगा। वास्तविक वस्तु डालने के बजाय – KutaBeach

+0

- भविष्य डालें, और आप सुरक्षित हैं। – jdevelop

0

वर्तमान कोड थ्रेड सुरक्षित नहीं है क्योंकि संदर्भ स्विच अगर और अगले के बीच हो सकता है। आपको clientTypesInitiated को ConcurrentHashMap टाइप करने की आवश्यकता है और इंटरफ़ेस मानचित्र नहीं है। और फिर putIfAbsent विधि

0

का उपयोग करें मुझे लगता है कि कोड बिल्कुल इरादे से काम नहीं करता है। देखो:

void onClientRequestReceived(int clientTypeIndex) { 
    // the line below ensures the existing data is lost even if was not null 
    if (clientTypesInitiated.put(clientTypeIndex, "") == null) { 
     Object clientTypeData = createClientTypeData(clientTypeIndex); 
     clientTypesInitiated.put(clientTypeIndex, clientTypeData); 
    } else { 
     processClientUsingClientTypeData(clientTypesInitiated.get(clientTypeIndex)); 
    } 
} 

मुझे लगता है कि आप वहां एक विधि विधि कॉल चाहते थे।

मैं इस तरह के दृष्टिकोण का सुझाव चाहते हैं:

void onClientRequestReceived(int clientTypeIndex) { 
    Object clientTypeData; 
    synchronized (clientTypesInitiated) { 
     if ((clientTypeData = clientTypesInitiated.get(clientTypeIndex)) == null) { 
      clientTypeData = createClientTypeData(clientTypeIndex); 
      clientTypesInitiated.put(clientTypeIndex, clientTypeData); 
     } 
    } 
    processClientUsingClientTypeData(clientTypeData); 
} 
0

putIfAbsent अच्छा काम करता है, लेकिन कभी कभी यह कई बार दोहराया प्रारंभ की आवश्यकता होगी। डबल-चेक किए गए लॉकिंग के साथ सिंक्रनाइज़ ब्लॉक शायद इस समस्या को हल कर सकता है।

public class Test { 

    private static Map<Integer, Object> clientTypesInitiated = new ConcurrentHashMap<Integer, Object>(); 

    private static final int CLIENT_TYPES_NUMBER = 10; 
    private static AtomicIntegerArray clientTypesInitStarted = new AtomicIntegerArray(CLIENT_TYPES_NUMBER); 
    private static AtomicIntegerArray clientTypesInitFinished = new AtomicIntegerArray(CLIENT_TYPES_NUMBER); 

    void onClientRequestReceived(int clientTypeIndex) { 

     int isInitStarted = clientTypesInitStarted.getAndIncrement(clientTypeIndex); 
     int isInitFinished = clientTypesInitFinished.get(clientTypeIndex); 

     if (isInitStarted == 0) { 
      //new client type index arrived, this type was never processed 
      //process data for that client type and put it into the map of types 
      Object clientTypeData = createClientTypeData(clientTypeIndex); 
      clientTypesInitiated.put(clientTypeIndex, clientTypeData); 
      clientTypesInitFinished.getAndIncrement(clientTypeIndex); 
     } else if (isInitFinished > 0) { 
      //already existing index - we already have results and we can use them 
      processClientUsingClientTypeData(clientTypesInitiated.get(clientTypeIndex)); 
     } 
    } 

    Object createClientTypeData(int clientIndex) { 
     return new Object(); 
    } 

    void processClientUsingClientTypeData(Object clientTypeData) { 
    } 
} 

कोई राय:

लेकिन यहाँ AtomicIntegerArrays के आधार पर एक और विकल्प है?

+0

मुझे लगता है कि आपको अधिक ऑब्जेक्ट उन्मुख कोड विकसित करने पर विचार करना चाहिए। 'ऑब्जेक्ट' संदर्भ का उपयोग करना एक अच्छा दिखने वाला समाधान नहीं है। एक अमूर्त वर्ग क्लाइंटडेटा बनाएं और इसे ठोस कक्षाओं को कार्यान्वित करने से प्राप्त करें। उन लोगों के लिए फ़ैक्टरी क्लास बनाएं जो 'request.class' के आधार पर' em को तुरंत चालू करते हैं। कोड जिसमें सामान्य रूप से प्रकार की गणना और अनुक्रमण है, रखरखाव और विस्तार में मुश्किल है। –

+0

एक गड़बड़ी के लिए खेद है, Krzysztof, ऑब्जेक्ट क्लाइंटडेटा के रूप में दिखाया जाने वाला कुछ डमी है, बेशक यह ऐसा नहीं होना चाहिए। कक्षाओं के लिए - वास्तव में प्रत्येक ग्राहक प्रकार संसाधन का एक सूचकांक है, क्लाइंट प्रकार बिल्कुल अलग नहीं हैं। मुझे उस संसाधन से संबंधित ग्राहकों को संसाधित करने से पहले कुछ संसाधनों को init करने की आवश्यकता है। हां, क्लाइंट टाइप के बजाय संसाधन इंडेक्स लिखना बेहतर होगा। – KutaBeach

+0

और अब वास्तविक कोड का जिक्र करते हुए, मुझे डर है कि यह छोटी है। देखो: पहला धागा 'if' में निष्पादित होता है। दूसरा तरीका विधि के माध्यम से चलता है, पहले 'if' छोड़ देता है।फिर यह दूसरी तुलना में विफल रहता है और ** ** ** अगर अन्यथा छोड़ देता है, तो प्रभावी रूप से कुछ भी नहीं करता है। –

1

ConcurrentHashMap, सिंक्रनाइज़ ब्लॉक का उपयोग नहीं बनाई गई है, हालांकि यह का उपयोग कर के रूप में सिंक्रनाइज़ ब्लॉक के लिए एक ताला ठीक है, लेकिन कुशल नहीं। यदि आपको ConcurrentHashMap का उपयोग करना है, तो सही उपयोग निम्नानुसार है;

private static Map<Integer, Object> clientTypesInitiated = new ConcurrentHashMap<Integer, Object>(); 

void onClientRequestReceived(int clientTypeIndex) { 
    Object clientTypeData = clientTypesInitiated.get(clientTypeIndex); 
    if (clientTypeData == null) { 
     Object newClientTypeData = createClientTypeData(clientTypeIndex); 
     clientTypeData = clientTypesInitiated.putIfAbsent(clientTypeIndex, newClientTypeData); 
     // if clientTypeData is null, then put successful, otherwise, use the old one 
     if (clientTypeData == null) { 
      clientTypeData = newClientTypeData; 
     } 
    } 
    processClientUsingClientTypeData(clientTypeData); 
} 

ईमानदारी से कहूं तो उपरोक्त कोड (दौड़ हालत की वजह से) clientTypeData कई बार बना सकता है, हालांकि यह synchronized ब्लॉक का उपयोग नहीं करता। इसलिए, आप को मापने चाहिए कि कैसे महंगा यह clientTypeData बनाने के लिए है और अगर यह इतना महंगा है कि यह synchronized ब्लॉक का उपयोग नहीं कर ढक है, तो आप भी ConcurrentHashMap उपयोग करने की आवश्यकता नहीं है। सामान्य HashMap और synchronized ब्लॉक का उपयोग करें।

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