자바 동시성
Java concurrency![]() |
자바 프로그래밍 언어와 자바 가상 머신(JVM)은 동시 프로그래밍을 지원하도록 설계되었으며, 모든 실행은 스레드의 맥락에서 이루어진다.개체와 리소스는 여러 개의 개별 스레드에 의해 액세스할 수 있다. 각 스레드는 고유한 실행 경로를 가지고 있지만 프로그램의 모든 개체에 잠재적으로 액세스할 수 있다.프로그래머는 객체에 대한 읽기 및 쓰기 액세스가 스레드 간에 적절하게 조정(또는 "동기화")되었는지 확인해야 한다.스레드 동기화는 한 번에 한 스레드에 의해 개체가 수정되고 다른 스레드에 의해 수정되는 동안 부분적으로 업데이트된 객체에 스레드가 액세스하지 못하도록 한다.자바 언어는 이러한 조정을 지원하기 위해 내장된 구조를 가지고 있다.
프로세스 및 스레드
Java 가상 머신의 대부분의 구현은 단일 프로세스로 실행되며 Java 프로그래밍 언어에서 동시 프로그래밍은 대부분 스레드(경량 프로세스라고도 함)와 관련이 있다.여러 JVM을 통해서만 여러 프로세스를 실현할 수 있다.
스레드 객체
스레드는 메모리와 열린 파일을 포함하여 프로세스의 리소스를 공유한다.이것은 효율적이긴 하지만 잠재적으로 문제가 될 수 있는 의사소통을 만든다.모든 어플리케이션에는 메인 스레드라고 불리는 적어도 하나의 스레드가 있다.메인 스레드는 다음과 같이 추가 스레드를 생성할 수 있다.Runnable
또는Callable
물건들(더)Callable
인터페이스와 유사함Runnable
이 두 가지 모두 다른 스레드에 의해 잠재적으로 실행될 수 있는 클래스에 대해 설계된다.aRunnable
그러나 결과를 반환하지 않으며 체크된 예외를 적용할 수 없다.)
각 스레드는 다른 CPU 코어에 예약하거나 단일 하드웨어 프로세서에서 시간-라이센싱 또는 많은 하드웨어 프로세서에서 시간-라이센싱을 사용할 수 있다.Java 스레드가 네이티브 OS 스레드에 매핑되는 방법에 대한 일반적인 해결책은 없다.모든 JVM 구현은 다른 방식으로 수행할 수 있다.
각 스레드는 스레드 클래스의 인스턴스와 연결된다.스레드 객체를 직접 사용하거나 다음과 같은 추상 메커니즘을 사용하여 스레드를 관리할 수 있음Executor
s와java.util.concurrent
수집품들
스레드 시작
스레드를 시작하는 두 가지 방법:
실행 가능한 개체 제공
공중의 계급 헬로런너블 기구들 런너블 { @오버라이드 공중의 공허하게 하다 달리다() { 시스템.밖으로.인쇄하다("실은 안녕!"); } 공중의 정태의 공허하게 하다 본래의(끈[] 아그) { (새로운 나사산(새로운 헬로런너블())).출발하다(); } }
서브클래스 나사산
공중의 계급 헬로스레드 연장하다 나사산 { @오버라이드 공중의 공허하게 하다 달리다() { 시스템.밖으로.인쇄하다("실은 안녕!"); } 공중의 정태의 공허하게 하다 본래의(끈[] 아그) { (새로운 헬로스레드()).출발하다(); } }
인터럽트
인터럽트는 자신이 하고 있는 일을 멈추고 다른 일을 해야 한다는 것을 실에 암시하는 것이다.스레드는 스레드가 중단되도록 스레드 객체에 인터럽트를 호출하여 인터럽트를 전송한다.인터럽트 메커니즘은 인터럽트 상태라고 알려진 내부 플래그를 사용하여 구현된다.호출Thread.interrupt
이 깃발을 꽂다관습에 의해, 어떤 방법이든, 투척으로 퇴장한다.InterruptedException
인터럽트 상태를 삭제한다.그러나 인터럽트를 호출하는 다른 스레드에 의해 인터럽트 상태가 즉시 다시 설정될 수 있다.
가입하다
그Thread.join
방법은 한 가닥이 다른 가닥의 완성을 기다리는 것을 허용한다.
예외
코드에서 검색되지 않은 예외는 스레드를 종료한다.그main
스레드는 콘솔에 예외를 인쇄하지만, 사용자가 만든 스레드는 이를 위해 등록된 처리기가 필요하다.[1][2]
메모리 모델
자바 메모리 모델은 자바 프로그래밍 언어의 스레드가 메모리를 통해 상호작용하는 방법을 설명한다.현대 플랫폼에서 코드는 작성된 순서대로 실행되지 않는 경우가 많다.최대 성능을 얻기 위해 컴파일러, 프로세서 및 메모리 서브시스템에 의해 재주문된다.Java 프로그래밍 언어는 공유 객체의 필드를 읽거나 쓸 때 선형성 또는 순차적 일관성을 보장하지 않으며, 이는 메모리 읽기의 순서를 변경하여 작동하는 컴파일러 최적화(등록기 할당, 공통 하위 표현 제거, 중복 읽기 제거 등)를 허용하기 위한 것이다.[3]
동기화
스레드는 주로 필드 및 참조 필드가 참조하는 객체에 대한 액세스를 공유하여 통신한다.이러한 형태의 통신은 매우 효율적이지만, 두 가지 종류의 오류를 가능하게 한다: 스레드 간섭과 메모리 일관성 오류.이러한 오류를 방지하는 데 필요한 도구는 동기화다.
리오더는 잘못 동기화된 다중 스레드 프로그램에서 실행될 수 있으며, 한 스레드는 다른 스레드의 영향을 관찰할 수 있으며, 가변 액세스가 실행되거나 프로그램에 지정된 순서와 다른 순서로 다른 스레드에 표시되는 것을 감지할 수 있다.대부분의 경우, 한 가닥은 다른 가닥이 무엇을 하고 있는지 신경 쓰지 않는다.하지만 그렇게 되면 동기화가 바로 그것이다.
스레드를 동기화하기 위해 Java는 모니터를 사용하는데, 이것은 한 번에 하나의 스레드만 모니터에 의해 보호되는 코드 영역을 실행할 수 있도록 허용하는 고도의 메커니즘이다.모니터의 동작은 자물쇠의 관점에서 설명된다; 각 물체와 관련된 자물쇠가 있다.
동기화에는 몇 가지 측면이 있다.가장 잘 이해되는 것은 상호 배제다. 단 하나의 나사산만이 모니터를 한 번에 잡을 수 있기 때문에 모니터에서 동기화하는 것은 한 나사산이 모니터에 의해 보호되는 동기화된 블록에 일단 들어가면 첫 번째 쓰레드가 동기화된 블록을 벗어날 때까지 다른 나사산이 해당 모니터에 의해 보호되는 블록에 들어갈 수 없다는 것을 의미한다.
그러나 동기화에는 상호 배제 이상의 것이 있다.동기화는 동기화된 블록 이전 또는 도중에 스레드에 의한 메모리 쓰기가 동일한 모니터에서 동기화하는 다른 스레드에 예측 가능한 방식으로 표시되도록 한다.동기화된 블록을 종료한 후 모니터를 해제하여 캐시를 메인 메모리로 플러시하는 효과가 있어 이 스레드에 의해 만들어진 쓰기가 다른 스레드에 보이도록 한다.동기화된 블록에 들어가기 전에 모니터를 획득하는데, 이것은 기본 메모리에서 변수가 다시 로드되도록 로컬 프로세서 캐시를 무효화하는 효과가 있다.그러면 우리는 이전 버전에서 볼 수 있는 모든 쓰기를 볼 수 있을 것이다.
읽기—필드가 휘발성이거나 모든 독서자와 작성자가 획득한 고유한 잠금 장치에 의해 필드가 보호되는 경우 필드로의 쓰기가 선형화될 수 있다.
잠금 및 동기화된 블록
스레드는 암묵적 잠금을 획득하는 동기화된 블록이나 메서드를 입력하거나 명시적 잠금(예: java.util.concurrent.locks 패키지의 ReentrantLock)을 획득하여 상호 배제를 달성할 수 있다.두 접근방식은 모두 기억 행동에 대해 동일한 의미를 갖는다.특정 필드에 대한 모든 액세스가 동일한 잠금에 의해 보호되는 경우, 해당 필드에 대한 쓰기—선형 가능(원자)
휘발성 필드
필드에 적용 시 Javavolatile
다음을 보장한다.
- (모든 Java 버전에서)휘발성 변수에 대한 읽기 및 쓰기에 대한 글로벌 주문이 있다.이는 휘발성 필드에 액세스하는 모든 스레드가 캐시된 값을 사용하는 대신 계속하기 전에 현재 값을 읽게 된다는 것을 의미한다.(단, 휘발성 읽기 및 쓰기의 상대적 순서에 대해서는 정기적인 읽기 및 쓰기를 보장하지 않으며, 이는 일반적으로 유용한 스레딩 구조가 아니라는 것을 의미한다.)
- (Java 5 이상에서) 휘발성 읽기 및 쓰기는 뮤텍스를 획득하고 해제하는 것과 마찬가지로 발생 전 관계를 설정한다.[4]이 관계는 한 특정 진술에 의한 기억 쓰기가 다른 특정 진술에 보일 수 있다는 보장에 불과하다.
휘발성 장은 선형화할 수 있다.휘발성 필드를 읽는 것은 잠금을 얻는 것과 같다. 작동 메모리는 무효화되고 휘발성 필드의 현재 값은 메모리에서 다시 읽힌다.휘발성 필드를 쓰는 것은 자물쇠를 푸는 것과 같다. 휘발성 필드는 즉시 기억으로 다시 기록된다.
최종 필드
최종으로 선언된 필드는 일단 초기화된 후에는 수정할 수 없다.개체의 최종 필드는 생성자에서 초기화된다.생성자가 특정 간단한 규칙을 따르는 경우, 동기화 없이 최종 필드의 정확한 값을 다른 스레드에 볼 수 있다.규칙은 간단하다:this
참조는 생성자가 반환하기 전에 생성자에서 해제되지 않아야 한다.
역사
JDK 1.2 이후 Java는 표준 집합의 수집 클래스인 Java 수집 프레임워크를 포함했다.
자바 컬렉션 프레임워크 구현에도 참여했던 더그 리아는 여러 개의 동시성 원시 요소와 수집 관련 클래스의 대용량 배터리로 구성된 동시성 패키지를 개발했다.[5]이 작업은 Doug Lea가 의장을 맡은 JSR 166의 일부로 계속 진행되었고 업데이트되었다.
JDK 5.0은 Java 동시성 모델에 많은 추가와 설명을 포함했다.JSR 166이 개발한 동시성 API도 처음으로 JDK의 일부로 포함되었다.JSR 133은 멀티스레드/멀티프로세서 환경에서 잘 정의된 원자력 운영을 지원했다.
Java SE 6와 Java SE 7 릴리스 모두 JSR 166 API의 업데이트된 버전과 몇 가지 새로운 추가 API를 도입하였다.
참고 항목
메모들
- ^ Oracle. "Interface Thread.UncaughtExceptionHandler". Retrieved 10 May 2014.
- ^ "Silent Thread death from unhandled exceptions". literatejava.com. Retrieved 10 May 2014.
- ^ 헐리, 모리스, 니르 샤빗."다중 프로세서 프로그래밍의 기술." PODC. 6. 2006.
- ^ 17.4.4: 동기화 순서
- ^ Doug Lea. "Overview of package util.concurrent Release 1.3.4". Retrieved 2011-01-01.
Note: Upon release of J2SE 5.0, this package enters maintenance mode: Only essential corrections will be released. J2SE5 package java.util.concurrent includes improved, more efficient, standardized versions of the main components in this package.
참조
- Goetz, Brian; Joshua Bloch; Joseph Bowbeer; Doug Lea; David Holmes; Tim Peierls (2006). Java Concurrency in Practice. Addison Wesley. ISBN 0-321-34960-1.
- Lea, Doug (1999). Concurrent Programming in Java: Design Principles and Patterns. Addison Wesley. ISBN 0-201-31009-0.