[입문 부터 포기 까지-자바]동시 프로 그래 밍-자물쇠-synchronized

간단 한 소개
전편[입문 부터 포기-자바]병행 프로 그래 밍-스 레 드 안전 에서 우 리 는 잠 금 체 제 를 통 해 공유 대상 을 보호 하고 스 레 드 안전 을 실현 할 수 있다 는 것 을 알 게 되 었 다.
synchronized 는 자바 가 제공 하 는 내 장 된 자물쇠 메커니즘 이다.synchronized 키 워드 를 통 해 코드 블록 을 동기 화 합 니 다.스 레 드 는 동기 코드 블록 에 들 어가 기 전에 자동 으로 자 물 쇠 를 얻 고 동기 코드 블록 을 종료 할 때 자동 으로 자 물 쇠 를 방출 합 니 다.내 장 된 자 물 쇠 는 일종 의 상호 배척 자물쇠 이다.
본문 은 synchronized 를 깊이 학습 합 니 다.
쓰다
동기 화 방법
동기 화 비정 상 방법
public class Synchronized {
    private static int count;

    private synchronized void add1() {
        count++;
        System.out.println(count);
    }

    public static void main(String[] args) throws InterruptedException {
        Synchronized sync = new Synchronized();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i< 10000; i++) {
                sync.add1();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i< 10000; i++) {
                sync.add1();

            }
        });

        thread1.start();
        thread2.start();
        Thread.sleep(1000);
        System.out.println(count);
    }
}

결 과 는 예상 에 부합 합 니 다.synchronized 는 비정 상 방법 에 작용 합 니 다.잠 금 된 것 은 인 스 턴 스 대상 입 니 다.위 에서 보 듯 이 잠 금 된 것 은 sync 대상 이기 때문에 스 레 드 가 정확하게 실 행 될 수 있 습 니 다.count 의 결 과 는 항상 20000 입 니 다.
public class Synchronized {
    private static int count;

    private synchronized void add1() {
        count++;
        System.out.println(count);
    }

    public static void main(String[] args) throws InterruptedException {
        Synchronized sync = new Synchronized();
        Synchronized sync1 = new Synchronized();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i< 10000; i++) {
                sync.add1();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i< 10000; i++) {
                sync1.add1();
            }
        });

        thread1.start();
        thread2.start();
        Thread.sleep(1000);
        System.out.println(count);
    }
}

결 과 는 기대 에 부합 되 지 않 습 니 다.위 에서 보 듯 이 비정 상 방법 에 작용 하고 자 물 쇠 는 정례 화 대상 이기 때문에 sync 와 sync 1 이 동시에 실 행 될 때 스 레 드 안전 문제 가 발생 할 수 있 습 니 다.자 물 쇠 는 두 개의 서로 다른 정례 화 대상 이기 때 문 입 니 다.
동기 정적 방법
public class Synchronized {
    private static int count;

    private static synchronized void add1() {
        count++;
        System.out.println(count);
    }

    private static synchronized void add11() {
        count++;
        System.out.println(count);
    }

    public static void main(String[] args) throws InterruptedException {
        Synchronized sync = new Synchronized();
        Synchronized sync1 = new Synchronized();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i< 10000; i++) {
                Synchronized.add1();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i< 10000; i++) {
                Synchronized.add11();

            }
        });

        thread1.start();
        thread2.start();
        Thread.sleep(1000);
        System.out.println(count);
    }
}

결 과 는 예상 에 부합 되 었 습 니 다.잠 금 정적 방법 일 때 잠 금 된 것 은 클래스 대상 입 니 다.따라서 서로 다른 스 레 드 에서 add 1 과 add 11 을 호출 하면 정확 한 결 과 를 얻 을 수 있 습 니 다.
동기 코드 블록
현재 인 스 턴 스 대상 잠 금
public class Synchronized {
    private static int count;

    private void add1() {
        synchronized (this) {
            count++;
            System.out.println(count);
        }
    }

    private static synchronized void add11() {
        count++;
        System.out.println(count);
    }

    public static void main(String[] args) throws InterruptedException {
        Synchronized sync = new Synchronized();
        Synchronized sync1 = new Synchronized();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i< 10000; i++) {
                sync.add1();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i< 10000; i++) {
                sync1.add1();
            }
        });

        thread1.start();
        thread2.start();
        Thread.sleep(1000);
        System.out.println(count);
    }
}

결 과 는 예상 에 부합 되 지 않 습 니 다.synchronized 동기 화 방법 블록 이 있 을 때 인 스 턴 스 대상 을 잠 갔 을 때 예 를 들 어 서로 다른 인 스 턴 스 에서 이 방법 을 호출 하면 스 레 드 안전 문제 가 발생 할 수 있 습 니 다.
다른 인 스 턴 스 대상 잠 금
public class Synchronized {
    private static int count;
    public String lock = new String();

    private void add1() {
        synchronized (lock) {
            count++;
            System.out.println(count);
        }
    }

    private static synchronized void add11() {
        count++;
        System.out.println(count);
    }

    public static void main(String[] args) throws InterruptedException {
        Synchronized sync = new Synchronized();
        Synchronized sync1 = new Synchronized();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i< 10000; i++) {
                sync.add1();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i< 10000; i++) {
                sync1.add1();
            }
        });

        thread1.start();
        thread2.start();
        Thread.sleep(1000);
        System.out.println(count);

        System.out.println(sync.lock == sync1.lock);
    }
}

결 과 는 예상 에 부합 되 지 않 습 니 다.synchronized 동기 화 방법 블록 이 있 을 때 다른 인 스 턴 스 대상 을 잠 갔 을 때 예 를 들 어 서로 다른 인 스 턴 스 에서 이 방법 을 호출 하면 스 레 드 안전 문제 가 발생 할 수 있 습 니 다.
public class Synchronized {
    private static int count;
    public String lock = "";

    private void add1() {
        synchronized (lock) {
            count++;
            System.out.println(count);
        }
    }

    private static synchronized void add11() {
        count++;
        System.out.println(count);
    }

    public static void main(String[] args) throws InterruptedException {
        Synchronized sync = new Synchronized();
        Synchronized sync1 = new Synchronized();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i< 10000; i++) {
                sync.add1();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i< 10000; i++) {
                sync1.add1();
            }
        });

        thread1.start();
        thread2.start();
        Thread.sleep(1000);
        System.out.println(count);

        System.out.println(sync.lock == sync1.lock);
    }
}

결 과 는 예상 에 부합 합 니 다.synchronized 동기 화 방법 블록 이 있 을 때 잠 긴 것 은 다른 인 스 턴 스 대상 이지 만 이미 인 스 턴 스 에 있 습 니 다.String="은 상수 탱크 에 저장 되 어 있 기 때문에 실제 잠 긴 것 은 같은 대상 이기 때문에 스 레 드 가 안전 합 니 다.
잠 금 클래스 개체
public class Synchronized {
    private static int count;

    private void add1() {
        synchronized (Synchronized.class) {
            count++;
            System.out.println(count);
        }
    }

    private static synchronized void add11() {
        count++;
        System.out.println(count);
    }

    public static void main(String[] args) throws InterruptedException {
        Synchronized sync = new Synchronized();
        Synchronized sync1 = new Synchronized();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i< 10000; i++) {
                sync.add1();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i< 10000; i++) {
                sync1.add1();
            }
        });

        thread1.start();
        thread2.start();
        Thread.sleep(1000);
        System.out.println(count);
    }
}

결 과 는 예상 에 부합 되 었 습 니 다.synchronized 동기 화 방법 블록 이 있 을 때 클래스 대상 을 잠 갔 을 때 예 를 들 어 서로 다른 인 스 턴 스 에서 이 방법 을 호출 하 는 것 은 스 레 드 가 안전 합 니 다.
자물쇠 메커니즘
public class Synchronized {
    private static int count;

    public static void main(String[] args) throws InterruptedException {
        synchronized (Synchronized.class) {
            count++;
        }
    }
}

javap-v Synchronized.class 를 사용 하여 class 파일 을 역 컴 파일 합 니 다.
synchronized 는 실제로 Monitorenter 와 Monitorexit 를 통 해 잠 금 체 제 를 실현 하 는 것 을 볼 수 있 습 니 다.같은 시각 에 감시 구역 에 한 라인 만 들 어 갈 수 있다.스 레 드 의 동기 화 를 확보 합 니 다.
정상 적 인 상황 에서 명령 4 가 감시 구역 에 들 어가 고 명령 14 가 감시 구역 에서 물 러 난 후에 명령 15 는 명령 23 return 으로 바로 뛰 어 내린다.
그러나 이상 한 상황 에서 이상 은 명령 18 로 넘 어가 명령 20monitorexit 에서 자 물 쇠 를 방출 하여 이상 이 발생 했 을 때 방출 되 지 않 는 상황 을 방지한다.이것 은 사실 synchronized 의 장점 이기 도 합 니 다.코드 실행 상황 이 어떻든 주동 적 으로 자 물 쇠 를 풀 어 주 는 것 을 잊 지 않 습 니 다.
Monitors 의 더 많은 원 리 를 알 고 싶 으 면 클릭 해서 보 세 요.
자물쇠 업그레이드
Monitor 는 운영 체제 의 Mutex lock 에 의존 하여 실현 되 기 때문에 비교적 무 거 운 조작 으로 시스템 을 커 널 상태 로 전환 해 야 하기 때문에 비용 이 매우 많이 든다.이에 따라 jdk 1.6 에 편향 자물쇠 와 경량급 자 물 쇠 를 도입 했다.synchronized 는 네 가지 상태 가 있 습 니 다.자물쇠 없 음->편향 자물쇠->경량급 자물쇠->중량급 자물쇠.
자물쇠 없 음
자원 을 잠 그 지 않 으 면 모든 스 레 드 에 접근 하고 수정 할 수 있 습 니 다.하지만 동시에 하나의 스 레 드 만 수정 할 수 있 습 니 다.
편향 자물쇠
잠 금 경쟁 이 강하 지 않 은 상황 에서 보통 하나의 스 레 드 는 같은 자 물 쇠 를 여러 번 가 져 옵 니 다.자 물 쇠 를 가 져 오 는 대 가 를 줄 이기 위해 편향 자 물 쇠 를 도입 하여 자바 대상 머리 에 자 물 쇠 를 가 져 오 는 스 레 드 의 threadID 를 기록 합 니 다.
  • 스 레 드 가 대상 헤드 의 threadID 가 존재 하 는 것 을 발 견 했 을 때.현재 스 레 드 와 같은 스 레 드 인지 판단 합 니 다.
  • 그렇다면 자 물 쇠 를 다시 추가 하거나 풀 필요 가 없다.
  • 그렇지 않 으 면 threadID 의 생존 여 부 를 판단 한다.생존 하지 않 음:잠 금 상태 로 설정 하고 다른 스 레 드 경쟁 설정 편향 잠 금.생존:threadID 스 택 정 보 를 찾 아 자 물 쇠 를 계속 가지 고 있어 야 하 는 지 판단 합 니 다.threadID 스 레 드 를 업그레이드 하 는 자 물 쇠 를 경량급 자물쇠 로 가 져 야 합 니 다.가지 고 있 지 않 으 면 잠 금 을 취소 하고 잠 금 이 없 는 상태 로 설정 하여 다른 스 레 드 경쟁 을 기다 리 도록 합 니 다.

  • 편향 잠 금 의 취소 작업 이 비교적 무 거 워 서 안전 점 에 들 어가 기 때문에 경쟁 이 치열 할 때 성능 에 영향 을 줄 수 있 습 니 다.-XX:-UseBiased Locking=false 에서 편향 잠 금 을 사용 하지 않 습 니 다.
    경량급 자물쇠
    편향 잠 금 이 경량급 잠 금 으로 업그레이드 되 었 을 때 다른 스 레 드 는 CAS 방식 으로 대상 헤드 를 설정 하여 잠 금 을 가 져 오 려 고 시도 합 니 다.
  • 현재 스 레 드 의 스 택 프레임 에 Lock Record 를 설정 하여 현재 대상 머리 에 있 는 mark word 의 복사 본 을 저장 합 니 다.
  • mark word 의 내용 을 lock record 에 복사 하고 케이스 를 사용 하여 mark word 의 바늘 을 lock record
  • 에 가리 키 려 고 시도 합 니 다.
  • 교체 에 성공 하면 편향 잠 금 가 져 오기
  • 교체 에 성공 하지 못 하면 일정 횟수 를 자전 재 시도 합 니 다.
  • 일정 횟수 를 자전 하거나 새로운 스 레 드 로 자 물 쇠 를 경쟁 할 때 경량급 자 물 쇠 는 중량급 자물쇠 로 팽창 한다.

  • CAS
    CAS 는 compare and swap(비교 및 교체)입 니 다.일종 의 낙관적 인 자물쇠 메커니즘 이다.보통 세 개의 값 이 있다.
  • V:메모리 의 실제 값
  • A:낡은 기대치
  • B:수정 할 새 값 이 V 와 A 가 같 을 때 V 를 B 로 교체 합 니 다.즉,메모리 의 실제 값 이 우리 의 예상 값 과 같 을 때 새 값 으로 대 체 됩 니 다.

  • CAS 는 메모리 의 값 이 A 이 고 B 가 되면 A 가 되 는 ABA 문 제 를 만 날 수 있 습 니 다.이때 A 는 새 값 이 므 로 교체 해 서 는 안 됩 니 다.A-1,B-2,A-3 의 방식 으로 이 문 제 를 피 할 수 있다.
    헤비급 자물쇠
    자전 은 CPU 를 소모 하기 때문에 한 동안 자전 하거나 한 스 레 드 가 자전 할 때 새로운 스 레 드 로 자 물 쇠 를 경쟁 하면 경량급 자 물 쇠 는 중량급 자물쇠 로 팽창 할 수 있다.헤비급 잠 금 은 Monitor 를 통 해 이 루어 집 니 다.Monitor 바 텀 은 실제 적 으로 운영 체제 에 의존 하 는 mutex lock(상호 배척 자물쇠)이 이 루어 집 니 다.사용자 상태 에서 커 널 상태 로 전환 해 야 하 는데 원가 가 비교적 높다.
    총결산
    본문 은 우리 가 함께 공부 했다.
  • synchronized 의 몇 가지 용법:동기 화 방법,동기 화 코드 블록.실제 동기 화 클래스 나 동기 화 인 스 턴 스 대상 입 니 다.
  • 자물쇠 업그레이드:자물쇠 없 음,편향 자물쇠,경량급 자물쇠,중량급 자물쇠 와 그 팽창 과정.

  • synchronized 는 내장 자물쇠 로 서 스 레 드 안전 문 제 를 해결 해 주 었 으 나 성능 의 손실 을 가 져 왔 으 므 로 남용 해 서 는 안 됩 니 다.사용 시 동기 블록 의 역할 범 위 를 주의 하 십시오.일반적으로 작용 범위 가 작 을 수록 성능 에 미 치 는 영향 도 적다.
    본문 저자:aloof
    원문 을 읽다
    본 고 는 운 서 지역사회 의 오리지널 내용 으로 허락 없 이 전재 할 수 없다.

    좋은 웹페이지 즐겨찾기