2010-01-04 86 views
14

有沒有辦法列出綁定到線程的TheadLocals?理想情況下,我可以訪問Thread.threadLocals地圖,但它受到封裝保護。java:list thread locals?

我需要這個的原因是我需要檢查線程,因爲它們返回到線程池以確保ThreadLocals已被正確清理。也許還有另一種方式來做到這一點?

回答

2

只要您知道要清理哪些變量,您可以使用線程池的afterExecute方法來執行任何清理(重新初始化?)。

否則,您可以使用反射 - 從線程內,遍歷您感興趣的類的聲明字段,併爲每個類型爲ThreadLocal的實例的0123.set對該對象的initialValue (s)你在乎。

1

我需要這個的原因是我需要檢查線程,因爲它們返回到線程池以確保ThreadLocals已被正確清理。

就我個人而言,我認爲在線程池線程上使用線程本地數據是一種不好的做法。如果你真的需要線程本地狀態,你應該自己管理線程,所以你可以明確地清理數據。

線程池線程具有不確定的生命週期,所以您不應該依賴顯式管理的本地線程數據。

+4

同意。但我想修復現有代碼中的錯誤,而不是重新設計它。 – 2010-01-04 18:59:06

0

您可以使用類AOP構造,通過創建一個Runnable實現,該實現通過您自己的實現來包裝原始的Runnable。它將調用原始的Runnable的run方法,然後在線程的上下文中執行所需的其他清理,這將允許您調用ThreadLocal.remove()方法。然後,給這個包裝到線程池。這適用於任何線程池實現(例如沒有before/afterExecute方法的實現)

+0

掛鉤到線程生命週期不是問題。我正在使用ThreadPoolExecutor,所以我有afterExecute方法來處理。問題給出了一個線程,我如何枚舉線程本地存儲?我想檢查它是否爲空,並且如果不記錄警告。 – 2010-01-04 19:51:12

+0

你是否可以控制線程本地使用(你能告訴開發者使用你自己的實現)嗎?您可能能夠擴展ThreadLocal以幫助實現您的目標。 – Armadillo 2010-01-06 15:44:11

0

從源代碼看,它看起來非常緊密。一切對於Thread或ThreadLocal都是私有的。

可能能夠通過重新定義ThreadLocal來添加一個方法,該方法將在當前線程上轉儲局部變量,從而通過工具代理執行您所需要的操作。

這裏有一個例子,我發現添加記錄到現有的類:http://today.java.net/pub/a/today/2008/04/24/add-logging-at-class-load-time-with-instrumentation.html

你需要使用BCELJavaAssist修補ThreadLocal的字節碼來添加方法。稍後,您需要使用反射來獲取方法的句柄,以便您可以調用它。

注:這個,如果你在一個受限制的環境(applet或應用服務器)作爲安全機制通常阻止你擺弄系統類運行可能不會工作。

2

沿着'另一個很好的方式來做到這一點',我做了一個Runnable包裝器,該包裝器獲取預先存在的線程本地的快照,運行嵌套的runnable,然後清除(設置爲null)任何線程local最初並不存在。

這可以通過將'快照'代碼放置到@Explorer中的beforeExecute()和'cleanup'代碼中來完成,就像@danben建議的那樣。

無論哪種方式,美麗的是,你不必硬編碼當地人保留或丟棄哪些線程。

異常處理已從源代碼清單中刪除以避免混亂。

 
public class ThreadLocalCleaningRunnable implements Runnable 
{ 
    private final Runnable runnable; 

    public ThreadLocalCleaningRunnable(Runnable runnable) { 
     this.runnable = nonNull(runnable); 
    } 

    public void run() { 
    // printThreadLocals(); 
     Set> initialThreadLocalKeys = getThreadLocalKeys(); 
     try { 
      runnable.run(); 
     } 
     finally { 
      cleanThreadLocalsExcept(initialThreadLocalKeys); 
     // printThreadLocals(); 
     } 
    } 

    public static void printThreadLocals() { 
     Thread thread = Thread.currentThread(); 

      Field threadLocalsField = Thread.class.getDeclaredField("threadLocals"); 
      threadLocalsField.setAccessible(true); 
      Class threadLocalMapKlazz = Class.forName("java.lang.ThreadLocal$ThreadLocalMap"); 
      Field tableField = threadLocalMapKlazz.getDeclaredField("table"); 
      tableField.setAccessible(true); 

      Object threadLocals = threadLocalsField.get(thread); 
      if (threadLocals != null) { 
       Object table = tableField.get(threadLocals); 
       if (table != null) { 
        int threadLocalCount = Array.getLength(table); 
        String threadName = thread.getName(); 

        for (int i = 0; i > getThreadLocalKeys() { 
     Thread thread = Thread.currentThread(); 

      Set> threadLocalKeys = new HashSet>(); 

      Field threadLocalsField = Thread.class.getDeclaredField("threadLocals"); 
      threadLocalsField.setAccessible(true); 
      Class threadLocalMapKlazz = Class.forName("java.lang.ThreadLocal$ThreadLocalMap"); 
      Field tableField = threadLocalMapKlazz.getDeclaredField("table"); 
      tableField.setAccessible(true); 

      Object threadLocals = threadLocalsField.get(thread); 
      if (threadLocals != null) { 
       Object table = tableField.get(threadLocals); 
       if (table != null) { 
        int threadLocalCount = Array.getLength(table); 

        for (int i = 0; i) entry).get(); 
          if (o instanceof ThreadLocal) { 
           threadLocalKeys.add((ThreadLocal) o); 
          } 
         } 
        } 
       } 
      } 
      return threadLocalKeys; 
    } 

    public static void cleanThreadLocalsExcept(Set> keptThreadLocalKeys) { 
     Thread thread = Thread.currentThread(); 

      Field threadLocalsField = Thread.class.getDeclaredField("threadLocals"); 
      threadLocalsField.setAccessible(true); 
      Class threadLocalMapKlazz = Class.forName("java.lang.ThreadLocal$ThreadLocalMap"); 
      Field tableField = threadLocalMapKlazz.getDeclaredField("table"); 
      tableField.setAccessible(true); 

      Object threadLocals = threadLocalsField.get(thread); 
      if (threadLocals != null) { 
       Object table = tableField.get(threadLocals); 
       if (table != null) { 
        int threadLocalCount = Array.getLength(table); 

        for (int i = 0; i) entry).get(); 
          if (o instanceof ThreadLocal) { 
           ThreadLocal tl = (ThreadLocal) o; 
           if (!keptThreadLocalKeys.contains(tl)) { 
            Field valueField = entry.getClass().getDeclaredField("value"); 
            valueField.setAccessible(true); 
            valueField.set(entry, null); 
           } 
          } 
         } 
        } 
       } 
      } 
    } 
} 

+0

順便說一句,我的需要是在一個線程池中也使用HtmlUnit(或Selenium無頭),該線程池也遭受線程局部內存泄漏。 – karmakaze 2012-06-08 19:09:28

+0

代碼不能編譯。 – mrswadge 2016-05-23 11:28:19

+0

關於代碼的Html消毒似乎已經搞砸了一下。在通用類型和尖括號周圍缺少字符。大部分都可以猜到。我會看看我是否可以再次找到該源並更新。 – karmakaze 2016-05-24 19:29:32