2011-09-20 54 views
3

我正在使用一个线程设置多个线程(服务)的设置,将它们一起运行以模拟系统的运行,然后在最后加入它们并处理终止等。我的测试作为服务之一运行,并通过JMS与其他服务进行通信。对于我的一个测试,我需要访问另一个线程中包含的私有变量。我无法将在其他线程中运行的代码更改为添加访问方法或通过JMS发送变量。由于框架设置的方式,我也没有办法将对希望访问的服务的引用传递到我的测试服务中。跨线程反射

我知道包含我需要访问的类的线程的名称,并且我可以通过枚举正在运行的线程来获取对线程的引用,但我不知道如何从线程一旦我得到它。

有什么方法可以让我使用反射或其他技术来获得对另一个线程中类的引用吗?

编辑:这里是对情况的例子我在:

import java.lang.reflect.Field; 

public class Runner 
{ 
    /** 
    * Pretend this is my test class. 
    */ 
    public static void main(String[] args) 
    { 
     // this is how my test starts up the system and runs the test 
     runTest(TestService.class); 
    } 

    /** 
    * Instantiate the test service and start up all of the threads in the 
    * system. Doesn't return until test has completed. 
    * 
    * @param testServiceClass 
    *   the class that will run the test 
    */ 
    static void runTest(Class<? extends Service> testServiceClass) 
    { 
     try 
     { 
      // setup the services 
      Service testService = 
        testServiceClass.getConstructor(new Class<?>[] { String.class }) 
          .newInstance("test service"); 

      FixedService fixedService = new FixedService("fixed service"); 

      // start the services 
      testService.start(); 
      fixedService.start(); 

      // wait for testService to signal that it is done 
      System.out.println("Started threads"); 
      while (!testService.isDone()) 
      { 
       try 
       { 
        Thread.sleep(1000); 
       } 
       catch (InterruptedException e) 
       { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
      } 
      // stop the fixed service 
      fixedService.stop(); 
      System.out.println("TestService done, fixed service told to shutdown"); 
     } 
     catch (Exception e) 
     { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 

    /** 
    * I cannot modify this class. Handling of thread start is similar to real 
    * system. 
    */ 
    abstract static class Service implements Runnable 
    { 
     protected boolean isDone = false; 
     protected boolean stop = false; 
     private Thread thisServiceThread; 

     public Service(String name) 
     { 
      thisServiceThread = new Thread(this, name); 
     } 

     public boolean isDone() 
     { 
      return isDone; 
     } 

     public void start() 
     { 
      thisServiceThread.start(); 
     } 

     public void stop() 
     { 
      this.stop = true; 
     } 
    } 

    /** 
    * I can modify this class. This is the class that actually runs my test. 
    */ 
    static class TestService extends Service 
    { 
     public TestService(String name) 
     { 
      super(name); 
     } 

     @Override 
     public void run() 
     { 
      System.out.println("TestService: started"); 

      // TODO: How can I access FixedService.getMe from where without 
      // modifying FixedService? 
      try 
      { 
       Field field = FixedService.class.getDeclaredField("getMe"); 
       field.setAccessible(true); 
       System.out.println(field.get(null)); 
      } 
      catch (SecurityException e) 
      { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      catch (NoSuchFieldException e) 
      { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      catch (IllegalArgumentException e) 
      { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      catch (IllegalAccessException e) 
      { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 

      System.out.println("TestService: done"); 
      isDone = true; 
     } 

    } 

    /** 
    * I cannot modify this class. This is part of the system being tested. 
    */ 
    static class FixedService extends Service 
    { 
     private boolean getMe = false; 

     public FixedService(String name) 
     { 
      super(name); 
     } 

     @Override 
     public void run() 
     { 
      System.out.println("FixedService: started"); 

      // don't stop until signaled to do so 
      while (!stop) 
      { 
       try 
       { 
        Thread.sleep(1000); 
       } 
       catch (InterruptedException e) 
       { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
      } 

      System.out.println("FixedService: gotMe? " + getMe); 

      System.out.println("FixedService: done"); 
      isDone = true; 
     } 
    } 
} 
+0

私有变量不属于线程。你能解释更多的小动物吗?你的意思是,Thread的子类的实际实例的成员变量? –

+0

@SteveMcLeod我的意思是一个Runnable类的实例的成员变量。 –

回答

3

正如Hemal Pandya所说,如果您想要实际读取或操作字段,您将需要服务对象,而不仅仅是类。

假设Object你需要的是在线程上设置的Runnable,这是可能的,有一些非常脏的反射黑客。您必须使用私有成员访问攻击从线程获取target字段,然后再次使用它来访问您在可运行的本身上需要的字段。

这是一些示例代码。请注意,我并没有真正考虑线程同步问题(尽管我不确定甚至可以正确同步这种访问)

import java.lang.reflect.Field; 

public class SSCCE { 
    static class T extends Thread { 
     private int i; 

     public T(int i) { 
      this.i = i; 
     } 

     @Override 
     public void run() { 
      while(true) { 
       System.out.println("T: " + i); 
       try { 
        Thread.sleep(1000); 
       } catch (InterruptedException e) { 
        // ignore 
       } 
      } 
     } 
    } 

    static class R implements Runnable { 
     private int i; 

     public R(int i) { 
      this.i = i; 
     } 

     @Override 
     public void run() { 
      while(true) { 
       System.out.println("R: " + i); 
       try { 
        Thread.sleep(1000); 
       } catch (InterruptedException e) { 
        // ignore 
       } 
      } 
     } 
    } 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     Thread t1 = new T(1); 
     Thread t2 = new Thread(new R(2)); 

     t1.start(); 
     t2.start(); 

     try { 
      Thread.sleep(3000); 
     } catch (InterruptedException e) { 
      // ignore 
     } 

     setI(t1,3); 
     setI(t2,4); 
    } 

    static void setI(Thread t, int newVal) { 
     // Secret sauce here... 
     try { 
      Field fTarget = Thread.class.getDeclaredField("target"); 
      fTarget.setAccessible(true); 
      Runnable r = (Runnable) fTarget.get(t); 

      // This handles the case that the service overrides the run() method 
      // in the thread instead of setting the target runnable 
      if (r == null) r = t; 

      Field fI = r.getClass().getDeclaredField("i"); 
      fI.setAccessible(true); 
      fI.setInt(r, newVal); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

} 
+0

这正是我所需要的。谢谢! –

0

这不是Thread而是你必须让变量out的Object。试着想出一个控制和控制线程的小代码示例,它会变得更清晰。

访问私有成员是很容易的部分。

编辑:使用MapThread.getId()Runnable。当你想检查一个线程时,识别可运行的,并使用反射。由于同步问题,您可能无法获得最新值。

+0

我知道如何访问私有成员,一旦我得到它所包含的实例的引用。我的问题是试图首先获得该引用。 –

+0

这就是为什么我说写一些示例代码:)如果没有它,很难提供任何建议,如果您编写足够小的示例,您可以自己找出解决方案。 –

+0

不幸的是,我无法访问创建线程的代码,所以我没有看到如何创建这样的映射。 –