2010-01-04 17 views
14

¿Hay alguna manera de listar TheadLocals enlazado a un hilo? Idealmente, podría acceder al mapa Thread.threadLocals, pero está protegido por paquete.java: list thread locals?

La razón por la que necesito esto es que necesito inspeccionar los hilos a medida que se devuelven a un grupo de subprocesos para garantizar que los ThreadLocals se hayan limpiado correctamente. Quizás hay otra forma de hacer esto?

Respuesta

2

Puede usar el método afterExecute del grupo de subprocesos para realizar cualquier limpieza (¿reinicialización?), Siempre que sepa qué variables desea limpiar.

lo contrario, podría utilizar la reflexión - desde el interior de la rosca, iterar a través de los campos declarados de la clase (s) usted está interesado en, y para cada uno cuyo tipo es una instancia de ThreadLocal, set a su initialValue en el objeto (s) que te importa

1

La razón por la que necesito esto es que necesito inspeccionar los hilos a medida que se devuelven a un grupo de subprocesos para garantizar que los ThreadLocals se hayan limpiado correctamente.

Personalmente, creo que utilizar los datos locales de subprocesos en threadpool threads es una mala práctica. Si realmente necesita un estado local de subprocesos, debe administrar los subprocesos por sí mismo, para que pueda limpiar los datos de forma explícita.

Los subprocesos del grupo de subprocesos tienen tiempos de vida indeterminados, por lo que no debe confiar en los datos de subprocesos locales administrados explícitamente.

+4

De acuerdo. Pero quiero arreglar un error en el código existente, no volver a diseñarlo. –

0

Puede utilizar una construcción similar a AOP, al crear una implementación Runnable que envuelve el Runnable original mediante su propia implementación. Invocará el método de ejecución del Runnable original y luego realizará cualquier otra limpieza que necesite desde el contexto del subproceso, lo que le permitirá llamar al método ThreadLocal.remove(). Luego, dale esta envoltura al grupo de subprocesos. Esto funcionaría para cualquier implementación de grupo de subprocesos (por ejemplo, los que no tienen los métodos before/afterExecute)

+0

Enganchar en el ciclo de vida del hilo no es el problema. Estoy usando ThreadPoolExecutor, entonces tengo el método afterExecute para trabajar. El problema se da un hilo, ¿cómo puedo enumerar el almacenamiento local de hilo? Me gustaría verificar que esté vacío y, si no, registrar una advertencia. –

+0

¿Tiene control sobre el uso local de la secuencia (puede decirles a los desarrolladores que usen su propia implementación)? Es posible que pueda extender ThreadLocal para ayudarlo a lograr su objetivo. – Armadillo

0

Desde las fuentes, parece ser bastante ajustado. Todo es privado para Thread o ThreadLocal.

puede hacer lo que necesita a través de un agente de instrumentación al redefinir ThreadLocal para agregar un método que volcará los locales en el hilo actual.

He aquí un ejemplo que encontré para añadir registro a una clase existente: http://today.java.net/pub/a/today/2008/04/24/add-logging-at-class-load-time-with-instrumentation.html

Tendrá que utilizar BCEL o JavaAssist parchear el código de bytes ThreadLocal añadir el método. Más adelante, tendrá que usar la reflexión para obtener un control del método para poder llamarlo.

Nota: esto probablemente no funcionará si está ejecutando en un entorno restringido (applet o servidor de aplicaciones) ya que los mecanismos de seguridad generalmente evitan que se pierda con las clases del sistema.

2

En la línea de "Otra buena manera de hacer esto", hice un contenedor Runnable que toma una instantánea de los locales preexistentes de hilos, ejecuta el ejecutable anidado, luego borra (establece en nulo) cualquier hilo local que no estaba inicialmente presente.

Esto podría hacerse mejor colocando el código de "instantánea" en el ejecutable de un ejecutable antes de Ejecutar() y el código de 'limpieza' en el comando de ejecución posterior como lo indica @danben.

De cualquier manera, la belleza es que no es necesario codificar qué personas locales guardar o desechar.

El manejo de excepciones se eliminó del listado de fuentes para evitar el desorden.

 
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

Por cierto, mi necesidad era usar HtmlUnit (o sin cabeza de selenio) en un grupo de subprocesos que también sufre de pérdidas de memoria local de subprocesos. – karmakaze

+0

El código no se compila. – mrswadge

+0

La desinfección de HTML en el código parece haberlo estropeado un poco. Faltan caracteres alrededor de los tipos genéricos y corchetes angulares. La mayor parte se puede adivinar. Veré si puedo encontrar esta fuente de nuevo y actualizar. – karmakaze

Cuestiones relacionadas