이 문제에 대한 훌륭한 설명이 2015 년 4 월 7 일자 Andrei Pangin 에 의해 작성되었습니다. 여기 에서 사용할 수 있지만 러시아어로 작성되어 있습니다 (어쨌든 코드 샘플을 검토하는 것이 좋습니다. 국제적입니다). 일반적인 문제는 클래스 초기화 중 잠금입니다.
다음은 기사의 인용문입니다.
JLS 에 따르면 모든 클래스에는 초기화 중에 캡처되는 고유 한 초기화 잠금 이 있습니다. 다른 스레드가 초기화 중에이 클래스에 액세스하려고하면 초기화가 완료 될 때까지 잠금에서 차단됩니다. 클래스가 동시에 초기화되면 교착 상태가 발생할 수 있습니다.
정수의 합을 계산하는 간단한 프로그램을 작성했는데 무엇을 인쇄해야합니까?
public class StreamSum {
static final int SUM = IntStream.range(0, 100).parallel().reduce((n, m) -> n + m).getAsInt();
public static void main(String[] args) {
System.out.println(SUM);
}
}
이제 제거 parallel()
람다를 하거나 Integer::sum
호출로 바꾸십시오. 무엇이 변경됩니까?
여기서 교착 상태가 다시 나타납니다. [이 기사에서 이전에 클래스 이니셜 라이저에 교착 상태의 몇 가지 예가있었습니다]. parallel()
스트림 작업은 별도의 스레드 풀에서 실행 되기 때문입니다 . 이 스레드는 바이트 코드로 작성된 람다 본문을 실행하려고합니다.private static
StreamSum
클래스 내부 메서드 . 그러나이 메서드는 스트림 완료 결과를 기다리는 클래스 정적 이니셜 라이저가 완료되기 전에는 실행할 수 없습니다.
더 놀라운 것은 무엇입니까?이 코드는 다른 환경에서 다르게 작동합니다. 단일 CPU 시스템에서 올바르게 작동하며 다중 CPU 시스템에서 중단 될 가능성이 높습니다. 이 차이는 Fork-Join 풀 구현에서 비롯됩니다. 매개 변수를 변경하여 직접 확인할 수 있습니다.-Djava.util.concurrent.ForkJoinPool.common.parallelism=N