2009-07-29 11 views
61

थ्रेडलोकल कैसे कार्यान्वित किया जाता है? क्या यह जावा में कार्यान्वित किया गया है (थ्रेडिड से ऑब्जेक्ट में कुछ समवर्ती मानचित्र का उपयोग करके), या क्या यह कुछ अधिक कुशलता से करने के लिए कुछ जेवीएम हुक का उपयोग करता है?जावा के थ्रेडलोकल हुड के तहत कैसे लागू किया गया है?

उत्तर

84

यहां सभी उत्तरों सही हैं, लेकिन थोड़ा निराशाजनक है क्योंकि वे कुछ हद तक चमकते हैं कि ThreadLocal का कार्यान्वयन कितना चालाक है। मैं सिर्फ source code for ThreadLocal देख रहा था और इसे लागू करने के तरीके से सुखद रूप से प्रभावित था।

अनुभवहीन कार्यान्वयन

अगर मैं एक ThreadLocal<T> वर्ग एपीआई जावाडोक में वर्णित दी लागू करने के लिए कहा था, आप क्या करेंगे? प्रारंभिक कार्यान्वयन Thread.currentThread() का उपयोग करके इसकी कुंजी के रूप में होगा। यह उचित रूप से अच्छी तरह से काम करेगा लेकिन कुछ नुकसान है।

  • थ्रेड विवाद - ConcurrentHashMap एक बहुत चालाक वर्ग है, लेकिन यह अंततः अभी भी किसी भी तरह से इसके साथ mucking से अधिक थ्रेड को रोकने से निपटने के लिए है, और अगर अलग धागे इसे नियमित रूप से हिट हुई, वहाँ मंदी हो जाएगा।
  • थ्रेड समाप्त होने के बाद भी, थ्रेड और ऑब्जेक्ट दोनों के लिए एक सूचक को स्थायी रूप से रखता है और जीसीड किया जा सकता है।

जीसी के अनुकूल कार्यान्वयन

ठीक कोशिश फिर से, weak references का उपयोग करके कचरा संग्रहण मुद्दे के साथ सौदा कर सकते हैं। WeakReferences के साथ काम कर भ्रमित हो सकते हैं, लेकिन यह एक नक्शा इतना की तरह बनाया का उपयोग करने के लिए पर्याप्त होना चाहिए: (! और हम होना चाहिए)

Collections.synchronizedMap(new WeakHashMap<Thread, T>()) 

या अगर हम Guava उपयोग कर रहे हैं:

new MapMaker().weakKeys().makeMap() 

यह का मतलब है कि एक बार थ्रेड पर कोई और नहीं हो रहा है (जिसका अर्थ है कि यह समाप्त हो गया है) कुंजी/मूल्य कचरा इकट्ठा किया जा सकता है, जो एक सुधार है, लेकिन अभी भी थ्रेड विवाद समस्या को संबोधित नहीं करता है, जिसका अर्थ है कि अब तक हमारे ThreadLocal सब कुछ नहीं है एक वर्ग के अद्भुत। इसके अलावा, अगर किसी ने समाप्त होने के बाद Thread ऑब्जेक्ट्स पर पकड़ने का निर्णय लिया है, तो वे कभी भी जीसीएड नहीं होंगे, और इसलिए न ही हमारी वस्तुएं होंगी, भले ही वे तकनीकी रूप से पहुंच योग्य नहीं हों।

चालाक कार्यान्वयन

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

कार्यान्वयन कुछ इस तरह दिखेगा:

// called for each thread, and updated by the ThreadLocal instance 
new WeakHashMap<ThreadLocal,T>() 

संगामिति यहाँ बारे में चिंता करने की कोई जरूरत नहीं है, क्योंकि केवल एक धागा कभी इस नक्शे तक पहुँचने की जाएगी।

जावा देवों का यहां हमारे ऊपर एक बड़ा लाभ है - वे सीधे थ्रेड क्लास विकसित कर सकते हैं और इसमें फ़ील्ड और ऑपरेशंस जोड़ सकते हैं, और यही वही है जो उन्होंने किया है।

java.lang.Thread में निम्नलिखित लाइनों है:

/* ThreadLocal values pertaining to this thread. This map is maintained 
* by the ThreadLocal class. */ 
ThreadLocal.ThreadLocalMap threadLocals = null; 

कौन सा रूप में टिप्पणी से पता चलता है वास्तव में सभी मूल्यों का एक पैकेज-निजी मानचित्रण इस Thread के लिए ThreadLocal वस्तुओं के द्वारा पता लगाया जा रहा है। ThreadLocalMap का कार्यान्वयन WeakHashMap नहीं है, लेकिन यह उसी मूल अनुबंध का पालन करता है, जिसमें कमजोर संदर्भ से इसकी चाबियां शामिल हैं।

ThreadLocal.get() तो तरह कार्यान्वित किया जाता है:

public T get() { 
    Thread t = Thread.currentThread(); 
    ThreadLocalMap map = getMap(t); 
    if (map != null) { 
     ThreadLocalMap.Entry e = map.getEntry(this); 
     if (e != null) { 
      @SuppressWarnings("unchecked") 
      T result = (T)e.value; 
      return result; 
     } 
    } 
    return setInitialValue(); 
} 

और ThreadLocal.setInitialValue() तो जैसे:

private T setInitialValue() { 
    T value = initialValue(); 
    Thread t = Thread.currentThread(); 
    ThreadLocalMap map = getMap(t); 
    if (map != null) 
     map.set(this, value); 
    else 
     createMap(t, value); 
    return value; 
} 

अनिवार्य रूप से, पकड़ करने के लिए इस थ्रेड में एक नक्शा का उपयोग सब हमारे ThreadLocal वस्तुओं। इस तरह, हमें अन्य थ्रेड (ThreadLocal में मूल्यों के बारे में चिंता करने की आवश्यकता नहीं है, सचमुच वर्तमान थ्रेड में केवल मूल्यों तक पहुंच सकते हैं) और इसलिए कोई सहमति नहीं है। इसके अलावा, Thread एक बार हो जाने पर, इसका नक्शा स्वचालित रूप से जीसीएड होगा और सभी स्थानीय वस्तुओं को साफ कर दिया जाएगा। भले ही Thread पर रखा गया हो, ThreadLocal ऑब्जेक्ट कमजोर संदर्भ द्वारा आयोजित किए जाते हैं, और जैसे ही ThreadLocal ऑब्जेक्ट दायरे से बाहर हो जाता है, इसे साफ़ किया जा सकता है।


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

टीएल; डॉThreadLocal का कार्यान्वयन बहुत अच्छा है, और आप पहली नज़र में सोचने से कहीं अधिक तेज/स्मार्ट हो सकते हैं।

यदि आपको यह उत्तर पसंद आया तो आप मेरी (कम विस्तृत) discussion of ThreadLocalRandom की भी सराहना कर सकते हैं।

Thread/ThreadLocal कोड के टुकड़े Oracle/OpenJDK's implementation of Java 8 से लिया।

+1

आपके उत्तर शानदार दिखते हैं लेकिन यह मेरे लिए अभी बहुत लंबा है। +1 और स्वीकार किया गया है, और मैं इसे बाद में पढ़ने के लिए अपने getpocket.com खाते में जोड़ रहा हूं। धन्यवाद! – ripper234

+0

मुझे थ्रेडलोकल जैसी चीज चाहिए जो मुझे मूल्यों की पूरी सूची तक पहुंचने दे, मुझे map.values ​​() की तरह। तो, मेरा निष्पक्ष कार्यान्वयन एक WeakHashMap <स्ट्रिंग, ऑब्जेक्ट> है जहां कुंजी Thread.currentThread() है getName()। यह थ्रेड के संदर्भ से बचाता है। अगर धागा दूर हो जाता है, तो कुछ भी थ्रेड का नाम नहीं रखता है (एक धारणा, मैं स्वीकार करता हूं) और मेरा मूल्य दूर हो जाएगा। – bmauter

+0

मैं वास्तव में [हाल ही में उस प्रभाव के लिए एक प्रश्न का उत्तर दिया] (http://stackoverflow.com/a/15654081/113632)। एक 'WeakHashMap ' कई समस्याओं का परिचय देता है, यह थ्रेडसेफ नहीं है, और यह मुख्य रूप से मुख्य वस्तुओं के उपयोग के लिए है जिसका उद्देश्य == ऑपरेटर का उपयोग करके ऑब्जेक्ट पहचान के लिए समान तरीके से परीक्षण करता है "- इसलिए वास्तव में 'थ्रेड' ऑब्जेक्ट का उपयोग एक कुंजी के रूप में करना बेहतर करें। मैं आपके उपयोग के मामले के ऊपर ऊपर वर्णित गुवा कमजोर मैप्स का उपयोग करने का सुझाव दूंगा। – dimo414

7

जावा में थ्रेडलोकल वेरिएबल Thread.currentThread() उदाहरण द्वारा आयोजित हैश मैप तक पहुंचकर काम करता है।

+0

सही नहीं है यही कारण है कि (या कम से कम यह किसी भी अब नहीं है)। Thread.currentThread() Thread.class में एक देशी कॉल है। इसके अलावा थ्रेड में "थ्रेडलोकैलमैप" है जो हैश कोड का एकल-बाल्टी (सरणी) है। यह ऑब्जेक्ट मानचित्र इंटरफ़ेस का समर्थन नहीं करता है। – user924272

+1

यह मूल रूप से मैंने जो कहा है। currentThread() एक थ्रेड इंस्टेंस देता है, जिसमें मानों के लिए थ्रेडलोकल्स का नक्शा होता है। –

+0

यह एक मानचित्र नहीं है! – user924272

29

आपका मतलब java.lang.ThreadLocal है। यह काफी सरल है, वास्तव में, यह सिर्फ Thread ऑब्जेक्ट के अंदर संग्रहीत नाम-मूल्य जोड़े का मानचित्र है (Thread.threadLocals फ़ील्ड देखें)। एपीआई उस कार्यान्वयन के विस्तार को छुपाता है, लेकिन यह सब कुछ कम है।

+0

तो, यह कोई ताले या विवाद नहीं करता है, है ना? – ripper234

+0

मैं नहीं देख सकता कि किसी की आवश्यकता क्यों होनी चाहिए, यह देखते हुए कि परिभाषा के अनुसार डेटा केवल एक धागे के लिए दृश्यमान होता है। – skaffman

+7

सही, कोई सिंक्रनाइज़ेशन या थ्रेडलोकैप मैप के अंदर या लॉकिंग नहीं है, क्योंकि यह केवल इन-थ्रेड तक पहुंच गया है। – Cowan

1

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

/* ThreadLocal values pertaining to this thread. This map is maintained 
* by the ThreadLocal class. */ 
ThreadLocal.ThreadLocalMap threadLocals = null; 

ThreadLocal.ThreadLocalMap यहाँ क्या है: और यह भी क्या जावा करता है? चूंकि आपके पास केवल threadLocals धागे के लिए है, इसलिए यदि आप threadLocals को अपने ThreadLocal (कहें, थ्रेड लोकल को Integer के रूप में परिभाषित करें) के रूप में लें, तो आपके पास केवल एक विशिष्ट थ्रेड के लिए एक ThreadLocal होगा। यदि आप थ्रेड के लिए एकाधिक ThreadLocal चर चाहते हैं तो क्या होगा? सबसे आसान तरीका threadLocalsHashMap बनाने के लिए है, प्रत्येक प्रविष्टि का keyThreadLocal चर का नाम है, और प्रत्येक प्रविष्टि के valueThreadLocal चर का मान है। थोड़ा उलझन में? मान लें कि हमारे पास दो धागे हैं, t1 और t2। वे Runnable उदाहरण Thread कन्स्ट्रक्टर के पैरामीटर के रूप में लेते हैं, और दोनों में tlA और tlb नामक चर हैं। यह वैसे ही है।

t1.tlA

+-----+-------+ 
| Key | Value | 
+-----+-------+ 
| tlA |  0 | 
| tlB |  1 | 
+-----+-------+ 

t2.tlB

+-----+-------+ 
| Key | Value | 
+-----+-------+ 
| tlA |  2 | 
| tlB |  3 | 
+-----+-------+ 

सूचना है कि मूल्यों मेरे द्वारा बने होते हैं।

अब यह सही लगता है। लेकिन ThreadLocal.ThreadLocalMap क्या है? यह सिर्फ HashMap का उपयोग क्यों नहीं किया? समस्या को हल करने के लिए, देखते हैं क्या होता है जब हम ThreadLocal वर्ग के set(T value) विधि के माध्यम से एक मूल्य निर्धारित करने की अनुमति:

public void set(T value) { 
    Thread t = Thread.currentThread(); 
    ThreadLocalMap map = getMap(t); 
    if (map != null) 
     map.set(this, value); 
    else 
     createMap(t, value); 
} 

getMap(t) बस t.threadLocals देता है। क्योंकि t.threadLocalsnull को initilized था, इसलिए हम createMap(t, value) पहले दर्ज करें:

void createMap(Thread t, T firstValue) { 
    t.threadLocals = new ThreadLocalMap(this, firstValue); 
} 

यह वर्तमान ThreadLocal उदाहरण और मूल्य का उपयोग कर एक नया ThreadLocalMap उदाहरण बनाता है स्थापित किया जाना। चलो देखते हैं क्या ThreadLocalMap है की तरह, यह ThreadLocal वर्ग

static class ThreadLocalMap { 

    /** 
    * The entries in this hash map extend WeakReference, using 
    * its main ref field as the key (which is always a 
    * ThreadLocal object). Note that null keys (i.e. entry.get() 
    * == null) mean that the key is no longer referenced, so the 
    * entry can be expunged from table. Such entries are referred to 
    * as "stale entries" in the code that follows. 
    */ 
    static class Entry extends WeakReference<ThreadLocal<?>> { 
     /** The value associated with this ThreadLocal. */ 
     Object value; 

     Entry(ThreadLocal<?> k, Object v) { 
      super(k); 
      value = v; 
     } 
    } 

    ... 

    /** 
    * Construct a new map initially containing (firstKey, firstValue). 
    * ThreadLocalMaps are constructed lazily, so we only create 
    * one when we have at least one entry to put in it. 
    */ 
    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { 
     table = new Entry[INITIAL_CAPACITY]; 
     int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); 
     table[i] = new Entry(firstKey, firstValue); 
     size = 1; 
     setThreshold(INITIAL_CAPACITY); 
    } 

    ... 

} 

ThreadLocalMap वर्ग के मुख्य भाग के तथ्य हिस्से में है Entry class, जो WeakReference फैली हुई है। यह सुनिश्चित करता है कि यदि वर्तमान धागा निकलता है, तो यह कचरा स्वचालित रूप से एकत्र किया जाएगा। यही कारण है कि यह HashMap के बजाय ThreadLocalMap का उपयोग करता है।यह Entry वर्ग के पैरामीटर के रूप में वर्तमान ThreadLocal और इसके मूल्य से गुजरता है, इसलिए जब हम मूल्य प्राप्त करना चाहते हैं, हम इसे table, जो Entry वर्ग का एक उदाहरण है से मिल सकता है:

public T get() { 
    Thread t = Thread.currentThread(); 
    ThreadLocalMap map = getMap(t); 
    if (map != null) { 
     ThreadLocalMap.Entry e = map.getEntry(this); 
     if (e != null) { 
      @SuppressWarnings("unchecked") 
      T result = (T)e.value; 
      return result; 
     } 
    } 
    return setInitialValue(); 
} 

यह वह जगह है ,

The Whole Picture

0

वैचारिक आप एक Map<Thread,T> कि धागे की विशिष्ट ग मान संग्रहीत कर लेता पकड़े के रूप में एक ThreadLocal<T> के बारे में सोच सकते हैं कि यह कैसे वास्तव में है, हालांकि यह नहीं है: क्या पूरी तस्वीर में की तरह है कार्यान्वित किया।

धागे-विशिष्ट मूल्यों को थ्रेड ऑब्जेक्ट में ही संग्रहीत किया जाता है; जब धागा समाप्त हो जाता है, तो धागे-विशिष्ट मूल्यों को कचरा इकट्ठा किया जा सकता है।

संदर्भ: JCIP

+0

संकल्पनात्मक रूप से, हां। लेकिन जैसा कि आप देखते हैं कि कार्यान्वयन के ऊपर अन्य उत्तर फॉर्म पूरी तरह अलग हैं। – Archit

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