2011-09-20 62 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

不幸的是,我無法訪問創建線程的代碼,所以我沒有看到如何創建這樣的映射。 –