2011-09-20 3 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 아니라 당신이 밖으로 변수를 얻을 수있는 Object되지 않습니다 : 저는 여기에 오전 상황의 예입니다. 제어 및 제어 스레드의 작은 코드 샘플을 생각해 보면 더 명확 해집니다.

개인 회원에 액세스하는 것은 쉬운 부분입니다.

편집 : Thread.getId()에서 Runnable까지 Map을 사용하십시오. 스레드를 검사하려면 실행 파일을 식별하고 리플렉션을 사용하십시오. 동기화 문제로 인해 최신 값을 얻지 못할 수도 있습니다.

+0

개인 멤버에 액세스하는 방법을 알았습니다. 포함 된 인스턴스에 대한 참조를 얻었습니다. 내 문제는 먼저 해당 참조를 얻으려고합니다. –

+0

그 이유는 내가 몇 가지 샘플 코드를 작성 :) 그것은없이 제안을 제공하기가 어렵습니다 당신이 솔루션을 직접 알아낼 수 있습니다 작은 샘플을 작성하는 경우 어렵습니다. –

+0

불행하게도, 스레드가 생성되는 코드에 액세스 할 수 없으므로 그러한 매핑을 어떻게 만들 수 있는지 알지 못합니다. –