고성능 병행 응용을 개발하는 것은 쉬운 일이 아니다.이러한 응용의 예는 고성능 웹 서버, 게임 서버와 검색엔진 파충류를 포함한다.

4490 단어
가시성
가시성 (visibility) 의 문제는 자바 멀티스레드 응용 프로그램의 오류의 근원입니다.단일 루틴 프로그램에서 변수의 값을 먼저 바꾸고 그 변수의 값을 읽을 때 읽은 값은 지난번에 쓴 값입니다.즉 앞의 조작 결과가 뒤의 조작에 대해 틀림없이 볼 수 있다는 것이다.그러나 다중 루틴 프로그램에서 일정한 동기화 메커니즘을 사용하지 않으면 한 루틴이 쓴 값이 다른 루틴에 대해 볼 수 있다는 것을 보장할 수 없다.이러한 상황을 초래한 원인은 다음과 같은 몇 가지가 있을 수 있다.
 
4
  • CPU 내부 캐시: 현재 CPU는 일반적으로 계층 구조의 몇 개의 캐시를 가지고 있습니다.CPU는 캐시에 있는 데이터를 직접 조작하고 필요할 때 캐시에 있는 데이터를 메모리와 동기화합니다.따라서 캐시에 있는 데이터와 메인 메모리에 있는 데이터가 일치하지 않을 수도 있습니다.스레드에서 수행된 쓰기 작업의 새 값은 현재 CPU 캐시에 저장되어 있고 메모리에 기록되지 않았을 수도 있습니다.이때, 다른 라인의 읽기 조작은 메인 메모리의 오래된 값을 읽는다

  • 4
  • CPU의 명령 실행 순서: 일부 경우 CPU가 명령의 실행 순서를 변경할 수 있습니다.이것은 한 라인이 다른 라인의 쓰기 작업이 끝난 후의 새로운 값을 너무 일찍 볼 수 있다

  • 4
  • 컴파일러 코드 재배열: 성능 최적화의 목적으로 컴파일러가 컴파일할 때 생성된 목표 코드를 재배열할 수 있다

  • 현실은 서로 다른 CPU가 서로 다른 구조를 채택할 수 있는데 이런 문제는 멀티코어 프로세서와 멀티프로세서 시스템에서 특히 복잡해진다는 것이다.한편, 자바의 목표는'한 번 작성하고 여기저기 실행하는 것'을 실현하는 것이기 때문에 자바 프로그램이 메모리에 접근하고 조작하는 방식을 규범화시켜 같은 프로그램이 서로 다른 CPU 구조에서 실행되는 결과가 일치하도록 할 필요가 있다.Java 메모리 모델(Java Memory Model)은 이러한 목적을 위해 도입된 것입니다.JSR 133은 이전 메모리 모델에 존재하는 문제점을 한층 더 수정했다.어쨌든 자바 메모리 모델은 프로그램에서 공유된 변수의 관계와 메인 메모리에서 이 변수의 값을 쓰고 읽는 밑바닥 세부 사항을 설명한다.자바 메모리 모델은 자바 언어의synchronized,volatile,final 등 키워드가 메모리의 변수 읽기와 쓰기 작업에 대한 의미를 정의한다.자바 개발자는 이 키워드를 사용하여 프로그램이 원하는 행동을 설명하고 컴파일러와 JVM은 생성된 코드가 실행할 때 메모리 모델에 부합되는 행동을 보장합니다.예를 들어volatile로 성명된 변수에 대해 읽기 전에 JVM은 CPU의 캐시 값이 먼저 효력을 상실하고 메인 메모리에서 다시 읽도록 확보한다.쓰기를 하면 새 값이 바로 메모리에 기록됩니다.synchronized와volatile 키워드도 컴파일러가 최적화될 때의 코드 리셋에 추가적인 제한을 준다.예를 들어 컴파일러는synchronized 블록의 코드를 옮길 수 없습니다.volatile 변수에 대한 읽기와 쓰기 동작은 다른 읽기와 쓰기와 함께 다시 배열할 수 없습니다.
     
    Java의 잠금
    데이터 경쟁이 존재할 때 가장 간단한 해결 방법은 잠금이다.잠금 메커니즘은 같은 시간에 한 라인만 경쟁 데이터가 발생하는 임계 구역에 접근할 수 있도록 제한한다.자바 언어의 synchronized 키워드는 코드 블록이나 방법을 잠글 수 있습니다.Java 객체에는 잠금 및 잠금 해제 작업을 수행할 수 있는 자체 모니터가 있습니다.synchronized 키워드로 보호된 코드 블록이나 방법이 실행되었을 때 현재 라인이 대상의 모니터에 있는 자물쇠를 성공적으로 가져왔음을 의미합니다.현재 블록이나 방법이 정상적으로 실행되거나 이상 퇴출이 발생할 때 현재 라인에서 가져온 자물쇠가 자동으로 방출됩니다.하나의 스레드는 자바 대상에 여러 번 자물쇠를 채울 수 있습니다.또한 JVM은 잠금을 가져오기 전과 해제한 후에 변수의 값이 메모리의 내용과 동기화된다는 것을 보장한다.
    Java 스레드 동기화
     
    어떤 경우, 단지 라인 사이의 데이터에 대한 상호 배척 접근만으로는 부족하다.일부 라인 사이에 협력 관계가 존재하기 때문에 일정한 협의에 따라 특정한 임무를 협동하여 완성해야 한다. 예를 들어 전형적인 생산자-소비자 모델이다.이 경우 자바에서 제공하는 스레드 사이의 대기 - 알림 메커니즘이 필요합니다.라인이 요구하는 조건이 충족되지 않으면 대기 상태로 들어간다.또 다른 라인은 적당한 시기에 알림을 보내 대기 중인 라인을 깨우는 것을 책임진다.Java의 java.lang.object 클래스의 wait/notify/notify All 방법 그룹은 라인 간의 동기화를 완성합니다.
    어떤 자바 대상에서wait 방법을 호출할 때, 우선 현재 라인이 이 대상의 자물쇠를 가져왔는지 확인해야 합니다.없으면 바로java를 던집니다.lang.Illegal Monitor State Exception 예외.자물쇠가 있으면 현재 라인을 대상의 대기 집합에 추가하고 자물쇠를 방출합니다.대상의 대기 집합에서 제거될 때까지 현재 라인이 막혀서 계속 실행할 수 없습니다.대상의 대기 집합에서 어떤 라인을 제거하는 원인은 여러 가지가 있습니다. 대상의 notify 방법이 호출되었을 때 이 라인이 선택됩니다.대상의 notifyAll 방법이 호출됨;노정이 끊기다;시간 제한이 있는wait 작업에 대해 시간 제한을 초과할 때;JVM 내부에서 비정상적인 작업을 수행합니다.
    위의 설명에서 몇 가지 결론을 얻을 수 있다.wait/notify/notify All 작업은synchronized 코드 블록이나 방법에 두어야 한다.wait/notify/notify All을 실행할 때 현재 라인이 필요한 자물쇠를 확보할 수 있다.어떤 대상의 대기 집합의 스레드 수를 파악하지 못할 때는 notify All 대신 notify All을 사용하는 것이 좋습니다.notifyAll은 불필요한 상황에서 라인이 깨어나 성능에 영향을 미치지만 사용은 더욱 간단하다.라인이 비정상적인 상황에서 의외로 깨어날 수 있기 때문에 일반적으로wait조작을 하나의 순환에 놓고 요구하는 논리적 조건이 만족하는지 검사해야 한다.일반적인 사용 패턴은 다음과 같습니다.
     
    private Object lock = new Object();
    synchronized (lock) { 
        while (/*            */) { 
           try { 
               lock.wait();  
           } catch (InterruptedException e) {} 
        } 
        //    
    }

    인터럽트 스레드
     
    하나의 라인 대상의interrupt () 방법을 통해 이 라인에 중단 요청을 할 수 있습니다.중단 요청은 일종의 라인 간의 협력 방식이다.스레드 A가 스레드 B의interrupt () 방법을 호출하여 중단 요청을 할 때, 스레드 A는 스레드 B의 주의를 요청합니다.라인 B는 편리할 때 이 중단 요청을 처리해야 한다. 물론 이것은 필수적인 것이 아니다.인터럽트가 발생할 때, 현재 인터럽트 상태를 기록하기 위해 루트 대상에 표시가 있습니다.isInterrupted() 방법으로 중단 요청이 발생했는지 여부를 판단할 수 있습니다.만약 중단 요청이 발생했을 때, 라인이 막힌 상태에 있다면, 이 중단 요청은 이 라인이 막힌 상태에서 물러나게 할 것입니다.라인이 막힌 상태를 초래할 수 있습니다. 라인이wait() 방법을 사용해서 대상의 대기 집합에 들어가거나,sleep() 방법을 통해 잠시 휴식을 취하거나,join() 방법을 통해 다른 라인이 완성되기를 기다릴 때입니다.라인이 막힌 상황에서 중단이 발생할 때java를 던집니다.lang.InterruptedException 은 코드가 해당 예외 처리 논리에 들어갑니다.실제로wait/sleep/join 방법을 호출할 때 이 이상을 포착해야 합니다.어떤 대상의 대기 집합을 중단하면 이 집합을 대기 집합에서 제거합니다. 다시 자물쇠를 얻은 후에 자바를 계속 실행할 수 있습니다.lang.InterruptedException 예외 처리 논리
    스레드를 중단하면 취소할 수 있는 작업을 수행할 수 있습니다.작업의 실행 과정에서 현재 라인의 중단 표시를 정기적으로 검사할 수 있으며, 라인이 중단 요청을 받으면 이 작업의 실행을 중지할 수 있습니다.자바를 만났을 때.lang.InterruptedException의 예외를 캡처한 후 처리하지 마십시오.이 차원에서 이 이상을 처리하고 싶지 않으면 이상을 다시 던져라.막힌 상태에서 라인이 끊기면java를 던집니다.lang.InterruptedException 예외일 경우 객체의 중단된 상태 표시가 비워집니다.자바를 잡으면.lang.InterruptedException이 이상하지만 다시 던질 수 없으면interrupt () 방법을 다시 호출해서 이 표시를 다시 설정해야 합니다.
     

    좋은 웹페이지 즐겨찾기