2010-01-29 2 views
2

안녕이의 ThreadLocal 행동

public class JdbcInterceptor { 
private static final JdbcInterceptor instance = new JdbcInterceptor(); 
private static ThreadLocal<Boolean> dontIntercept = new ThreadLocal<Boolean>(); 
    public static JdbcInterceptor getInstance() { 
return instance; 
} 
public void skipIntercept() { 
dontIntercept.set(true); 
} 
public boolean interrupt() { 
boolean di = dontIntercept.get()!=null?dontIntercept.get().booleanValue():false; 
if (di) { 
     dontIntercept.set(false); 
     } 
return di; 
}// end interrupt 
}// end class 

같은 클래스 정의가 그리고 내가 Class2의에서이 작업을 수행

//class1 stuff 
JdbcInterceptor.getInstance().skipIntercept(); 
if(JdbcInterceptor.getInstance().interrupt()) 
{ // class1 stuff happens 
} 

이제 다른 클래스 1에서이 작업을 수행

//class2 stuff 
if(JdbcInterceptor.getInstance().interrupt()) 
{ // class2 stuff happens 
} 

이제 혼란스러워서 수업 1 일이 일어날 것입니다. dontIntercept ThreadLocal. 나의 의심은 class2가 일어날 것인가 아닌가이다. 내 논리는 JdbcInterceptor의 인스턴스가 하나뿐이므로 interrupt()에 대한 모든 호출에서 동일한 상태를 사용할 수 있어야한다는 것입니다. 그러나 ThreadLocals은 각 스레드마다 로컬이라는 말을 들었습니다. 나는 여기서 약간의 갈등을 본다. 이것 좀 도와주세요.

답변

4

귀하의 질문이 명확하지 않습니다.

그러나 ThreadLocals는 각 스레드에 대해 로컬이라고 들었습니다. 나는 여기서 약간의 갈등을 본다.

올바른 내용입니다. 갈등이 없습니다. ThreadLocal와 함께 저장된 값 오브젝트는 각 스레드마다 고유합니다. 실제로 Thread 개체와 함께 내부적으로 저장되므로 스레드가 종료 될 때 모두 스레드 로컬 값은 다른 스레드에서 다른 참조가없는 한 가비지 수집을 위해 제거되고 사용할 수 있습니다.

두 스레드가 같은 스레드로 실행되는 경우 호출간에 로컬 스레드 값을 변경하지 않으면 두 스레드 모두 동일한 결과를 얻습니다. 만약 다른 쓰레드가 class2를 실행한다면, 두 클래스는 다른 값을 가질 것이다.

아마도 시간과 클래스/메소드 저장 및 코드에서 발생하는 스레드 실행이 혼란 스러울 수 있습니다. 이 두 가지는 매우 다릅니다. 하나의 스레드는 모든 메소드를 실행할 수 있으며 하나의 클래스에서 동일한 메소드를 실행하는 많은 스레드를 가질 수 있습니다. 코드에서 스레드를 "볼"수 없습니다. 다른 그림이 없다면 상상해보아야합니다. 약간의 시각적 시각화가 필요합니다. 이제

당신은 당신의 코드를이처럼 ThreadLocal 초기화를 사용하여 좀 명확하게 할 수 있습니다 로컬 스레드를 사용할 때 너무처럼 null 인 경우 확인할 필요가 없습니다

private static ThreadLocal<Boolean> dontIntercept = new ThreadLocal<Boolean>() { 

    @Override 
    protected Boolean initialValue() { 
     return Boolean.FALSE; 
    } 

}; 

를 :

Thread1 start---class1(skip=false)-+-skip(true)---+class1(true)--+class2(true)----+-end 
        |    |    |    |    | 
        Thread2 start---+-class1(false)+--------------+class2(false)---+-class1(false)---end 
,536 : 여기
public boolean interrupt() { 
    return dontIntercept.get().booleanValue(); 
}// end interrupt 

이 더 두 개의 스레드를 실행할 수있는 방법을 보여주기위한 시도이다

classN (val)을 표시하면 값은 건너 뛰기 스레드 로컬 변수가 그 시간에 설정 한 값입니다.

이 예제에서보다 구체적으로 질문에 대답하려면 : thread1에 의해 실행될 때 class1 및 class2 코드는 모두 건너 뜁니다. thread2에 의해 실행될 때 그들은 건너 뛰지 않을 것입니다.

InheritableThreadLocal이라는 다른 유형의 스레드 로컬이 있음에 유의하십시오.이 예에서 다르게 동작합니다. 스레드 2은 스레드가 두 번째 스레드를 시작할 때 스레드 1에있는 값을 상속합니다.

코드가 class1에있는 경우 항상 건너 뛰기 값을 true로 설정하면 동작이 약간 변경됩니다. 스레드가 먼저 class1을 실행 한 다음 class2를 실행하면 건너 뛰기는 true이됩니다. 스레드가 먼저 class2를 실행 한 다음 class1을 실행하면 스킵은 앞의 경우 false이고 나중에 대해서는 true이됩니다. 건너 뛰기를 false으로 되돌릴 수있는 방법이 있다는 것을 나타내지는 않습니다.

질문하기. 실제로 모든 스레드에 대해 정확히 동일한 상태를 원하면 ThreadLocal을 사용하지 마십시오. 일반 변수를 사용하고 volatile으로 표시하거나 동기화로 보호하십시오.

+0

충돌로 인해 클래스의 인스턴스가 하나만있는 것과 다른 멤버 변수 값을 반환하는 것 사이의 충돌을 의미합니다. –

+0

내가 어떻게 설정되어 있는지 보여주기 위해 편집 된 코드는 false로 건너 뜁니다. –

+0

그것이 의도 한 것이면 충돌이 아닙니다. ThreadLocals는 그 목적으로 사용됩니다. 새 코드에서는 class1에서 실행 한 이후 이벤트 시퀀스가 ​​다르다. 값은 항상 'true'로 설정되고, 메소드는 건너 뛰고 'false'로 재설정된다. 새 코드를 사용하면 class1의 코드를 제거하고 class2의 'if'체크를 제거 할 필요가 없습니다. –