2011-08-31 15 views
19

से प्राप्त किए गए नए थ्रेड पर थ्रेडलोकल का प्रचार करना मैं एक निष्पादक सेवा और भविष्य (उदाहरण कोड here) का उपयोग कर एक अलग थ्रेड में एक प्रक्रिया चला रहा हूं (थ्रेड "स्पॉन्गिंग" एओपी पहलू में होता है)।एक निष्पादक सेवा

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

थ्रेडलोकल चर का उपयोग करने के लिए सबसे अच्छा तरीका क्या होगा थ्रेडलोकल चर का उपयोग नए थ्रेड में रीस्टेसी द्वारा किया जाता है? ऐसा लगता है कि संदर्भ जानकारी का ट्रैक रखने के लिए Resteasy एक से अधिक थ्रेडलोकल चर का उपयोग करता है और मैं सभी जानकारी को नए धागे में "अंधाधुंध" स्थानांतरित करना चाहता हूं।

मैंने ThreadPoolExecutor उपclassing और पूल में वर्तमान धागे को पारित करने के लिए beforeExecute विधि का उपयोग करके देखा है, लेकिन मुझे पूल में थ्रेडलोकल चर को पारित करने का कोई तरीका नहीं मिला।

कोई सुझाव?

धन्यवाद

+0

आप दूसरा पैराग्राफ थोड़ा पुनर्लेखन सकते हैं? यह मुझे भ्रमित कर रहा है। इसके अलावा, पहले निष्पादन के साथ क्या गलत है? आप इसे ठीक से काम नहीं कर पाए, या आपको एहसास हुआ कि यह आपकी ज़रूरत के अनुरूप नहीं हो सकता है? – toto2

उत्तर

13

ThreadLocal एक धागा के साथ जुड़े उदाहरणों के सेट निजी सदस्यों में आयोजित की जाती हैं प्रत्येक Thread का। इन्हें गिनने का आपका एकमात्र मौका Thread पर कुछ प्रतिबिंब करना है; इस तरह, आप थ्रेड के फ़ील्ड पर एक्सेस प्रतिबंधों को ओवरराइड कर सकते हैं।

एक बार जब आप ThreadLocal के सेट प्राप्त कर सकते हैं, तो आप ThreadPoolExecutor की beforeExecute() और afterExecute() हुक का उपयोग कर पृष्ठभूमि धागे में नकल कर सकता है, या कि run() कॉल को बीच में रोक एक सेट नहीं स्थापित करने के लिए अपने कार्यों के लिए एक Runnable आवरण बनाने के द्वारा आवश्यक ThreadLocal उदाहरणों। दरअसल, बाद की तकनीक बेहतर काम कर सकती है, क्योंकि यह कार्य कतार के समय ThreadLocal मानों को स्टोर करने के लिए सुविधाजनक स्थान प्रदान करेगा।


अद्यतन: यहाँ दूसरा दृष्टिकोण का एक और अधिक ठोस उदाहरण दिया गया है। मेरे मूल विवरण के विपरीत, रैपर में संग्रहीत सभी कॉलिंग थ्रेड है, जिसे कार्य निष्पादित होने पर पूछताछ की जाती है।

static Runnable wrap(Runnable task) 
{ 
    Thread caller = Thread.currentThread(); 
    return() -> { 
    Iterable<ThreadLocal<?>> vars = copy(caller); 
    try { 
     task.run(); 
    } 
    finally { 
     for (ThreadLocal<?> var : vars) 
     var.remove(); 
    } 
    }; 
} 

/** 
* For each {@code ThreadLocal} in the specified thread, copy the thread's 
* value to the current thread. 
* 
* @param caller the calling thread 
* @return all of the {@code ThreadLocal} instances that are set on current thread 
*/ 
private static Collection<ThreadLocal<?>> copy(Thread caller) 
{ 
    /* Use a nasty bunch of reflection to do this. */ 
    throw new UnsupportedOperationException(); 
} 
+0

क्या आप एक उदाहरण दे सकते हैं कि बाद की तकनीक कैसे काम करेगी? – Viraj

+0

@ वीराज मैंने कुछ कोड जोड़ा। क्या उससे मदद हुई? – erickson

+0

हां। निश्चित रूप से। – Viraj

2

मैं अपनी समस्या को समझने के रूप में, आप InheritableThreadLocal पर एक नज़र जो आप ThreadLocal कोड को देखें, तो आप देख सकते हैं बाल थ्रेड संदर्भ

+15

काम नहीं करेगा, पहले ओपी के पास तीसरे पक्ष पुस्तकालय में 'थ्रेडलोकल' निर्माण पर नियंत्रण नहीं है। दूसरा, 'निष्पादक सेवा' थ्रेड का पुन: उपयोग करता है, जबकि 'इनहेरटेबल थ्रेडलोकल' केवल तभी काम करता है जब आप सीधे नए धागे को जन्म देते हैं। –

-1

के जनक थ्रेड संदर्भ से ThreadLocal चर पारित करने के लिए है हो सकता है :

public T get() { 
     Thread t = Thread.currentThread(); 
     ... 
    } 

वर्तमान धागा ओवरराइट नहीं किया जा सकता है।

संभावित समाधान: जावा 7 दोराहे पर

  1. देखो/तंत्र में शामिल होने के (लेकिन मुझे लगता है कि यह एक बुरा तरीका है) endorsed तंत्र पर

  2. देखो अपने JVM में ThreadLocal वर्ग अधिलेखित करने के लिए।

  3. कोशिश के पुनर्लेखन के लिए RESTEasy (यदि आप अपने आईडीई में Refactor उपकरणों का उपयोग सभी ThreadLocal उपयोग को बदलने के लिए कर सकते हैं, यह आसान की तरह लग रहा है)

1

@ एरिक्सन उत्तर के आधार पर मैंने यह कोड लिखा था। यह विरासत थ्रेडलोकल्स के लिए काम कर रहा है। यह थ्रेड कन्स्ट्रक्टर में उपयोग की जाने वाली विधि के समान विरासत थ्रेडलोकल्स की सूची बनाता है। बेशक मैं ऐसा करने के लिए प्रतिबिंब का उपयोग करता हूं। इसके अलावा मैं निष्पादक वर्ग को ओवरराइड करता हूं।

public class MyThreadPoolExecutor extends ThreadPoolExecutor 
{ 
    @Override 
    public void execute(Runnable command) 
    { 
     super.execute(new Wrapped(command, Thread.currentThread())); 
    } 
} 

आवरण:

private class Wrapped implements Runnable 
    { 
     private final Runnable task; 

     private final Thread caller; 

     public Wrapped(Runnable task, Thread caller) 
     { 
     this.task = task; 
     this.caller = caller; 
     } 

     public void run() 
     { 
     Iterable<ThreadLocal<?>> vars = null; 
     try 
     { 
      vars = copy(caller); 
     } 
     catch (Exception e) 
     { 
      throw new RuntimeException("error when coping Threads", e); 
     } 
     try { 
      task.run(); 
     } 
     finally { 
      for (ThreadLocal<?> var : vars) 
       var.remove(); 
     } 
     } 
    } 

प्रतिलिपि विधि: डिफ़ॉल्ट

public static Iterable<ThreadLocal<?>> copy(Thread caller) throws Exception 
    { 
     List<ThreadLocal<?>> threadLocals = new ArrayList<>(); 
     Field field = Thread.class.getDeclaredField("inheritableThreadLocals"); 
     field.setAccessible(true); 
     Object map = field.get(caller); 
     Field table = Class.forName("java.lang.ThreadLocal$ThreadLocalMap").getDeclaredField("table"); 
     table.setAccessible(true); 

     Method method = ThreadLocal.class 
       .getDeclaredMethod("createInheritedMap", Class.forName("java.lang.ThreadLocal$ThreadLocalMap")); 
     method.setAccessible(true); 
     Object o = method.invoke(null, map); 

     Field field2 = Thread.class.getDeclaredField("inheritableThreadLocals"); 
     field2.setAccessible(true); 
     field2.set(Thread.currentThread(), o); 

     Object tbl = table.get(o); 
     int length = Array.getLength(tbl); 
     for (int i = 0; i < length; i++) 
     { 
     Object entry = Array.get(tbl, i); 
     Object value = null; 
     if (entry != null) 
     { 
      Method referentField = Class.forName("java.lang.ThreadLocal$ThreadLocalMap$Entry").getMethod(
        "get"); 
      referentField.setAccessible(true); 
      value = referentField.invoke(entry); 
      threadLocals.add((ThreadLocal<?>) value); 
     } 
     } 
     return threadLocals; 
    } 
0

यहाँ बच्चे धागा CompletableFuture द्वारा फैला करने के लिए माता पिता के सूत्र में वर्तमान LocaleContext पारित करने के लिए एक उदाहरण है [तक इसका इस्तेमाल किया ForkJoinPool]।

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

यहां समस्या पूरी थ्रेडलोकल की प्रतिलिपि नहीं है। केवल LocaleContext की प्रतिलिपि बनाई गई है। चूंकि थ्रेडलोकल केवल थ्रेड के लिए निजी पहुंच का है, यह प्रतिबिंब का उपयोग करके भी है और बच्चे को पाने और सेट करने की कोशिश करना बहुत ही बेकार सामान है जो स्मृति रिसाव या प्रदर्शन हिट का कारण बन सकता है।

तो यदि आप थ्रेडलोकल से रुचि रखने वाले पैरामीटर जानते हैं, तो यह समाधान तरीका क्लीनर काम करता है।

public void parentClassMethod(Request request) { 
     LocaleContext currentLocale = LocaleContextHolder.getLocaleContext(); 
     executeInChildThread(() -> { 
       LocaleContextHolder.setLocaleContext(currentLocale); 
       //Do whatever else you wanna do 
      })); 

     //Continue stuff you want to do with parent thread 
} 


private void executeInChildThread(Runnable runnable) { 
    try { 
     CompletableFuture.runAsync(runnable) 
      .get(); 
    } catch (Exception e) { 
     LOGGER.error("something is wrong"); 
    } 
} 
0

मुझे प्रतिबिंब दृष्टिकोण पसंद नहीं है। वैकल्पिक समाधान निष्पादक रैपर को कार्यान्वित करना और ऑब्जेक्ट को सीधे ThreadLocal संदर्भ के रूप में संदर्भित करना होगा, जो कि मूल संदर्भ का प्रचार करने वाले सभी बच्चों के धागे के संदर्भ में है।

public class PropagatedObject { 

    private ThreadLocal<ConcurrentHashMap<AbsorbedObjectType, Object>> data = new ThreadLocal<>(); 

    //put, set, merge methods, etc 

} 

==>

public class ObjectAwareExecutor extends AbstractExecutorService { 

    private final ExecutorService delegate; 
    private final PropagatedObject objectAbsorber; 

    public ObjectAwareExecutor(ExecutorService delegate, PropagatedObject objectAbsorber){ 
     this.delegate = delegate; 
     this.objectAbsorber = objectAbsorber; 
    } 
    @Override 
    public void execute(final Runnable command) { 

     final ConcurrentHashMap<String, Object> parentContext = objectAbsorber.get(); 
     delegate.execute(() -> { 
      try{ 
       objectAbsorber.set(parentContext); 
       command.run(); 
      }finally { 
       parentContext.putAll(objectAbsorber.get()); 
       objectAbsorber.clean(); 
      } 
     }); 
     objectAbsorber.merge(parentContext); 
    } 
संबंधित मुद्दे