노트 읽기-Java 동시 프로그래밍 전투-2 장 스레드 안전성

스레드 안전 문제에 대한 전제 :

이 변수는 수정할 수 있습니다.

변수는 여러 스레드에서 액세스됩니다.

여러 스레드가 적절한 동기화없이 동일한 변수 상태 변수에 액세스하면 프로그램에서 오류가 발생합니다. 이 문제를 해결하는 방법에는 세 가지가 있습니다.

  • 스레드간에 상태 변수를 공유하지 마십시오.
  • 상태 변수를 변경 불가능한 변수로 수정
  • 방문시 동기화 사용

1. 스레드 안전성이란 무엇입니까?

스레드 안전성 : 여러 스레드가 특정 클래스에 액세스하면 클래스가 올바르게 작동 할 수 있으며이 클래스를 스레드 안전성이라고합니다.

상태 비 저장 개체는 스레드로부터 안전해야합니다.

(Stateful 및 Stateless : Stateful은 데이터 저장 기능이있는 클래스를 의미합니다. Stateless는 클래스의 속성이 데이터를 저장하지 않음을 의미합니다.)

2. 원 자성

1. 경쟁 조건 : 여러 스레드가 동일한 리소스에 액세스 할 때 스레드의 실행 순서가 다르면 결과가 일치하지 않을 수 있으며 이때 경쟁 조건이 발생합니다. 여러 스레드에서 액세스하는 코드 리소스를 중요 섹션이라고합니다.

  • 먼저 확인한 후 실행 : 실패 할 수있는 관찰을 기반으로 판단하거나 특정 작업을 수행합니다. 이러한 종류의 경쟁 조건을 실행 전 검사라고합니다.

2. 지연 초기화의 경쟁 조건 : 지연 초기화는 메모리를 절약하기 위해 필요한 경우 객체를 초기화하는 동시에 객체가 한 번만 초기화되도록해야합니다. 그러나 멀티 스레딩의 경우 두 스레드가 동시에 초기화 조건을 실행하고, 첫 번째 스레드의 초기화가 완료되지 않은 경우 두 번째 스레드가 이때 조건 판단을 수행합니다. 그 결과 객체가 초기화되지 않은 경우 두 번째 스레드가 초기화를 다시 수행합니다. 현재 스레드 안전성 문제가 발생했습니다. 한 번 실행되어야하는 코드는 두 개의 스레드에 의해 한 번 실행되었습니다. 첫 번째와 두 번째 스레드의 초기화가 완료되기 전에 초기화 조건을 판단 할 다른 스레드가있는 경우 그런 다음 한 번 더 초기화가 수행됩니다. 이것은 우리가보고 싶지 않은 상황입니다.

3. 복합 연산 : 원자 적으로 수행해야하는 여러 작업의 조합을 복합 연산이라고합니다. 예를 들어, 먼저 확인한 다음 실행하면 원자 적으로 실행할 일련의 작업이 있습니다. 따라서 먼저 확인한 다음 실행은 복합 작업입니다. 스레드 안전성을 보장하려면 각 복합 연산 그룹이 원자 적으로 실행되도록해야합니다. 일반적으로 우리는 잠금을 통해이 목표를 달성 할 것입니다.

세, 잠금 장치

1. 내장 잠금 : Java는 원 자성을 지원하는 내장 잠금 메커니즘 (동기화 된 코드 블록)을 제공합니다. 동기화 코드 블록은 두 부분으로 구성됩니다. 하나는 잠금의 개체 참조이고 다른 하나는 잠금의 보호 코드 블록입니다. Synchronized 키워드로 수정 된 메서드는 전체 메서드 본문을 포함하는 동기화 메서드입니다. 이 코드 블록의 잠금은이 메서드를 호출하는 인스턴스입니다. 바로 이것이다. 동기화 된 수정이 정적 메서드 인 경우 코드 블록의 잠금은 현재 클래스의 .CLASS 개체입니다.

2. 재진입 : 스레드가 다른 스레드가 보유한 잠금을 요청하면 요청 스레드가 차단됩니다. 그러나 내부 잠금은 재진입이 가능하기 때문에 스레드가 이미 보유하고있는 잠금을 획득하려고 시도하면 요청이 성공합니다. 재진입을 달성하는 한 가지 방법은 잠금을 카운터 및 소유자 스레드와 연관시키는 것입니다. 카운터가 0이면 어떤 스레드도 잠금을 유지하지 않습니다. 이때 잠금을 적용한 모든 스레드는 성공할 수 있습니다. 동시에 JVM은 잠금 보유자를 기록하고 카운터를 1로 설정합니다. 동일한 스레드가 다시 발생하면 이 잠금을 획득하면 카운터가 증가하고 스레드가 동기화 코드 블록을 종료하면 카운터가 0에 도달하고 잠금이 해제 될 때까지 카운터가 감소합니다.

3. 재진입의 이점 : 다음 코드와 같이 하위 클래스가 상위 클래스의 동기화 메서드를 재정의하는 경우. 하위 클래스 LoggingWidget의 doSomething을 실행할 때 스레드는 잠금을 얻습니다. 서브 클래스의 doSomething이 실행되면 부모의 doSomething이 다시 호출됩니다. 기본 제공 잠금이 재진입이 아닌 경우 상위 클래스의 doSomething은 영원히 대기합니다. 재진입은 이러한 종류의 교착 상태를 방지합니다.

public class Widget{
    
    
    public synchronized void doSomething(){
    
    
        ...
    }
}

public class LoggingWidget extends Widget{
    
    
    public synchronized void doSomething(){
    
    
        system.out.println("=================");
        super.doSomething();
    }
}

넷째, 자물쇠를 사용하여 상태를 보호하십시오.

한 번에 하나의 스레드 만이 변수를 작동하도록 보장하는 잠금으로 변수를 보호 할 수 있습니다. 스레드가 변수에 대한 작업을 완료 한 경우에만 다른 스레드가 변수에 대해 작업 할 수 있습니다.

5. 활동 및 성과

  • 잠금은 실제로 우리 프로그램의 보안을 보장 할 수 있습니다. 성능 향상을 위해 멀티 스레딩을 사용하고 있기 때문에 보안을 고려해야합니다. 멀티 스레딩의 경우 스레드 안전 문제가 발생하므로 동기화를 보장하기 위해 잠금이 필요합니다. 그러나 이것이 안전을 위해 성능을 희생해야한다는 의미는 아닙니다. 따라서 우리는 성능을 보장하면서 프로그램의 보안을 보장해야합니다. 맹목적인 보안을 위해 성능을 희생하지 마십시오. 그런 다음 멀티 스레딩을 도입했지만 말이되지 않습니다.
  • 따라서 잠글 때 동기화 코드 블록의 크기를 합리적으로 판단해야합니다. 이 시점에서 보안, 단순성 및 성능 간의 균형을 찾아야합니다. 단순성과 성능 사이에는 종종 충돌이 있습니다.

참고 : 빠르게 완료되지 않을 수있는 장기 계산 또는 작업 (네트워크 I / O 또는 콘솔 I / O)을 수행 할 때는 잠금을 유지하지 마십시오.

추천

출처blog.csdn.net/weixin_45373852/article/details/108726264