2010-01-04 4 views
3

내 응용 프로그램에서 병 목을 가리키는 핀이 있는데, 내게는 Thread::setContextClassLoader으로 전화가 온다.setContextClassLoader가 동시에 호출 될 때 속도가 크게 느려짐

기본적으로 제 3 자 라이브러리와 관련된 문제로 인해 스레드의 컨텍스트 클래스 로더가 엉망입니다. 이유를 이해하려면 this question을 참조하십시오.

내 지식에 대한 공통의 하나였다 그리고이 같은 작동 집어 솔루션 : 그것은 단지 하나의 스레드가 실행되었을 때 setContextClassLoader에 대한 호출이 문제가 아니라는 것을 밝혀졌다

Thread thread = Thread.currentThread(); 
ClassLoader old = thread.getContextClassLoader(); 
thread.setContextClassLoader(newClassLoader); 

try { 
    ... // problematic code that uses the thread context class loader 
} finally { 
    thread.setContextClassLoader(old); 
} 

을하지만, 둘 이상의 쓰레드가 그것을 할 때, 그것은 크게 느려집니다.

내가 문제를 분리하기 위해 다음과 같은 테스트 응용 프로그램을했습니다 :

ArrayList<Thread> threads = new ArrayList<Thread>(); 
int thread_count = 1; 

long start = System.currentTimeMillis(); 

for (int i = 0; i < thread_count; i++) { 
    Thread thread = new Thread(new MyRunnable(100000000)); 

    thread.start(); 
    threads.add(thread); 
} 

for (Thread thread : threads) { 
    thread.join(); 
} 

long total = System.currentTimeMillis() - start; 
double seconds = (double)total/1000; 

System.out.println("time in seconds: " + seconds); 

을 그리고 이것은 MyRunnable 클래스 :

public class MyRunnable implements Runnable { 
    int _iterations; 

    public MyRunnable(int iterations) { 
     _iterations = iterations; 
    } 

    public void run() { 
     final Thread curr = Thread.currentThread(); 
     final ClassLoader loader = ClassLoader.getSystemClassLoader(); 

     for (int i = 0; i < _iterations; i++) { 
      curr.setContextClassLoader(loader); 
     } 
    } 
} 

는 기본적으로 스레드의 몇 가지를 열고, 현재의 스레드를 설정 컨텍스트 클래스 로더를 루프의 시스템 클래스 로더에 추가합니다.

내 컴퓨터에서 코드를 변경 한 후 업데이트 된 결과 : thread_count이 1 일 때 0.5 초가 지나면 완료됩니다. 2 개의 스레드는 1.5 ~ 3 스레드 2.7 ~, 4 스레드는 4 ~ - 당신은 그림을 얻습니다.

스레드의 setContextClassLoader 구현을 살펴 보았습니다. 전달 된 클래스 로더에 멤버 변수가 설정되어있는 것으로 보입니다. 둘 이상의 스레드에서 실행할 때 그러한 오버 헤드를 설명하기 위해 잠금 (또는 공유 리소스에 대한 액세스)이 없음을 발견했습니다.

무엇이 여기에 있습니까?

P. 나는 JRE 1.5를 사용하고 있지만 1.6에서 똑같은 일이 일어난다.

편집 : @Tom Hawtin - 내가 언급 한 이유를 배제하기 위해 만든 코드 변경 사항을 확인하십시오. 시스템 클래스 로더를 한 번 가져 오더라도 스레드 수가> 1 일 때 결과가 더 느려집니다.

답변

3

소스에서 실제로 분명한 것은 Thread.setContextClassLoader과 관련이 없습니다. ClassLoader.getSystemClassLoader은 시스템 클래스 로더가 이미 초기화 된 경우에도 ClassLoader.class을 잠그는 initSystemClassLoader을 호출합니다.

잠재적 인 문제는 휘발성 변수를 읽는 것이 일부 다중 프로세서 시스템에 성능에 영향을 미칠 수 있다는 것입니다.

여기에서 우리는 단지 수백 사이클을보고 있습니다.

+0

겨우 수백 사이클이지만 사용자 코드의 대부분은 단지 몇 사이클 일 뿐이므로 자신이보고있는 속도 저하를 쉽게 일으킬 수 있습니다. 또한 잠금은 병렬 처리를 효과적으로 배제합니다. 예, setContextClassLoader() 호출은 시간에 추가되지만 실제 응용 프로그램에서는 단 한 번의 루프에서 천만 번 호출하지 않습니다. 사용량은 앱의 총 사용량 중 미세한 부분 일 것입니다. 그것에 대해 걱정하지 마십시오. –

+0

getSystemClassLoader를 호출하는 대신 빈 URLClassLoader를 작성하십시오. 동일한 결과가 표시됩니다. –

+0

'ClassLoader system = ClassLoader.getSystemClassLoader();'를 루프 밖에서 (그리고 그것을 사용하여) 추가하면 대부분 문제가 해결됩니다. 또한 모든 경우의 성능이 크게 향상됩니다. –

2

최근 JVM은 "편향 잠금"이라는 기술을 사용합니다.이 기술은 한 스레드 만 주어진 잠금에 액세스하면 잠금 획득을 거의 무료로 만듭니다. 두 번째 스레드가 처음으로 잠금에 액세스하려고하면 원래 접근 자에 대한 "편향"이 취소되고 잠금은 획득하기 위해 원자 작업을 필요로하는 정상/경량 잠금이됩니다.

바이어스 된 잠금과 일반적인 잠금의 성능 차이는 측정과 일치하는 크기 순서 (예 : 5 사이클 대 50 사이클) 일 수 있습니다.문제의 잠금 장치는 아마도 first reply에 언급 된 잠금 장치 일 것입니다. 바이어스 된 잠금에 대한 자세한 내용은 here을 참조하십시오.

바이어스 된 잠금을 무시하는 경우에도 동일한 잠금을 얻으려고하는 두 개의 스레드 또는 더 많은 스레드는 일반적으로 단일 스레드보다 총 처리량이 상당히 느립니다 (잠금 단어가 포함 된 캐시 라인과 경쟁하기 때문에).

+0

업데이트 된 코드보기 - getSystemClassLoader()를 한 번만 호출해도 결과는 동일하게 유지됩니다. –

+0

물론, 왜 결과가 더 느릴 것이라고 기대하지 않습니까? Tom이 지적한 것처럼, 수행되는 작업은 스레드의 수에 비례합니다. 4 번 스레드를 반복하면 4 번 반복됩니다. 몇 개의 CPU가 있습니까? 하이퍼 스레딩을 사용합니까? 대부분의 스레드가 더 일찍 완료 될 수 있지만 지터가 발생하여 모든 CPU가 완료 될 때까지 테스트가 끝나지 않습니다. – BeeOnRope

관련 문제