자물쇠 및 synchronized

6730 단어

자물쇠의 흔한 개념

  • 상호 배척: 같은 시간에 한 라인만 실행
  • 임계 구역: 상호 배척 실행이 필요한 코드
  • 세립도 자물쇠: 서로 다른 자물쇠로 보호된 자원을 정밀하게 관리한다.세립도 자물쇠는 병행도를 높일 수 있어 성능 최적화의 중요한 수단
  • 사라진 자물쇠: 서로 경쟁하는 자원의 라인이 서로 기다리기 때문에'영구적으로'막히는 현상을 초래한다.

  • 자물쇠로 하는 최고의 실천

  • 대상의 구성원 변수만 영원히 갱신할 때 잠금.
  • 가변적인 구성원 변수에 접근할 때만 자물쇠를 채웁니다.
  • 다른 대상의 방법을 영원히 사용하지 않을 때 잠금을 추가합니다.
  • 소득 보유 시간을 줄이고 자물쇠의 입도를 줄인다.

  • 동기식 및 비동기식

  • 호출 방법은 결과를 기다릴 필요가 있으면 동기화한다.결과를 기다릴 필요가 없다면 비동기적이다.
  • 동기화는 Java 코드의 기본 처리 방식입니다.

  • 비동기식 프로그램을 지원하는 방법:
  • 비동기 호출: 호출자가 하위 라인을 만들고 하위 라인에서 실행하는 방법을 호출합니다.
  • 비동기 방법: 피호출자;방법이 실현될 때, 라인이 주요 논리를 실행하고, 주 라인이 직접return을 실행하는 것을 보여 줍니다.

  • synchronized

    class  X{
        // 
        synchronized void foo(){
           // 
        }
        // 
        synchronized static void bar(){
           // 
        }
        
        // 
        Object obj = new Object();
        void baz(){
            synchronized(obj){
                // 
            }
        }
    }

    자바 컴파일러는synchronized에서 수식하는 방법이나 코드 블록의 앞뒤에 자동으로 잠금lock()과 잠금해제unlock()를 추가합니다. 이렇게 하는 장점은 잠금해제()와 잠금해제unlock()는 틀림없이 쌍을 이루고 나타날 것입니다. 잠금해제unlock()는 치명적인 Bug(다른 라인은 죽을 수밖에 없다는 것을 의미합니다.)
    코스메틱 정적 방법:
    // 
    class  X{
        // 
        synchronized(X.class) static void bar(){
           // 
        }
    }

    코스메틱 고정되지 않은 방법:
    // 
    class  X{
        // 
        synchronized(this) static void bar(){
           // 
        }
    }

    어떻게 자물쇠로 여러 자원을 보호합니까
    보호된 자원과 자물쇠 사이의 합리적인 관련 관계는 N:1의 관계이다. 즉, 한 개의 자물쇠로 여러 자원을 보호할 수 있지만 여러 개의 자물쇠로 한 자원을 보호할 수 없다.

    자물쇠를 사용하는 정확한 자세


    이체 업무를 예로 들다
    예 1:
    public class Account {
        /**
         * : 
         */
        private final   Object  balLock = new Object();
        /**
         *  
         */
        private Integer balance;
       
        /**
         *  
         *  this, 
         * this this.balance,  target.balance
         * 
         */
       synchronized void transfer(Account target,int amt){
            if (this.balance > amt) {
                this.balance -= amt;
                target.balance += amt;// , 
            }
        }
    }
    

    예 2:
    public class Account {
        /**
         * : 
         */
        private final   Object  balLock = new Object();
        /**
         *  
         */
        private Integer balance;
       
    
        /**
         *  , 
         *
         * Account.class Account ,
         *  Java Account ,
         *  
         *
         *  : 
         */
        void transfer2(Account target,int amt){
            synchronized(Account.class){
                if (this.balance > amt) {
                    this.balance -= amt;
                    target.balance += amt;
                }
            }
        }
    }

    이렇게 하면 이체 조작은 직렬이 되고 정상적인 논리는 계좌 이체와 계좌 이체만 잠궈야 한다.다른 이체 작업에 영향을 주지 않습니다.약간의 변경 사항:
    예 3:
    public class Account {
        /**
         * : 
         */
        private final Object lock;
        /**
         *  
         */
        private Integer balance;
       
        // 
        private Account(){}
        // lock 
        private Account(Object lock){
            this.lock = lock;
        }
        
        /**
         *  
         */
        void transfer(Account target,int amt){
            // 
            synchronized(lock){
                if (this.balance > amt) {
                    this.balance -= amt;
                    target.balance += amt;
                }
            }
        }
    }

    이 방법은 문제를 해결할 수 있지만, Account 대상을 만들 때 같은 대상에게 전송해야 합니다.
    그리고 전달 대상이 너무 번거롭고 쓰기가 번거로워 타당성이 부족하다.
    예 4:
    public class Account {
        
        /**
         *  
         */
        private Integer balance;
        
        /**
         *  
         */
        void transfer(Account target,int amt){
            // 
            synchronized(Account.class){
                if (this.balance > amt) {
                    this.balance -= amt;
                    target.balance += amt;
                }
            }
        }
    }

    어카운트로.class는 공유된 자물쇠로서 잠긴 범위가 너무 넓습니다.Account.class는 모든 계정 대상이 공유하고, 이 대상은 자바 가상기가 계정 클래스를 불러올 때 만든 것이기 때문에 유일성을 걱정할 필요가 없습니다.Account을 사용합니다.class는 공유 자물쇠로서 계정 대상을 만들 때 전송할 필요가 없습니다.
    이렇게 새로운 문제가 생겼어요. 어카운트를 쓰지만.class는 상호 배척 자물쇠로서 은행 업무 중의 이체 문제를 해결한다. 이 방안은 병발 문제가 존재하지 않지만 모든 계좌의 이체 조작은 직렬이다. 예를 들어 계좌 A이체 계좌 B, 계좌 C이체 계좌 D 두 이체 조작은 현실 세계에서 병행할 수 있지만 이 방안에서 직렬화되었다. 이렇게 하면 성능이 너무 떨어진다.그래서 병발량을 생각하면 이런 방법도 안 돼요.
    올바른 쓰기 방법은 다음과 같습니다.
    예 5:
    public class Account {
        
        /**
         *  
         */
        private Integer balance;
        
        /**
         *  
         */
        void transfer(Account target,int amt){
            // 
            synchronized(this){
                 // 
                synchronized(target){
                    if (this.balance > amt) {
                        this.balance -= amt;
                        target.balance += amt;
                    }
                }
            }
        }
    }

    우리는 고대에 정보화가 없으면 계좌의 존재 형식이 정말 하나의 장부였고 모든 계좌에 하나의 장부가 있었으며 이런 장부들은 모두 서류함에 통일적으로 보관되었다고 생각해 보았다.은행 카운터는 우리에게 이체를 할 때 서류함에 가서 이체 장부와 이체 장부를 모두 손에 넣은 후에 이체를 해야 한다.이 점원은 장부를 가져올 때 다음과 같은 세 가지 상황을 만날 수 있다.
  • 서류함에 마침 장부 이체와 장부 이체가 있으면 동시에 가져간다.
  • 만약에 서류함에 장부 이체와 장부 이체 중 하나만 있다면 이 점원은 서류함에 있는 장부를 먼저 손에 쥐고 다른 점원이 다른 장부를 돌려보내기를 기다린다.
  • 이체와 이체도 없으면 이 점원은 두 개의 장부가 모두 돌아오기를 기다리고 있다.

  • 세립도 자물쇠가 사라질 수도 있어요.

  • 사라진 자물쇠: 서로 경쟁하는 자원의 라인이 서로 기다리기 때문에'영구적으로'막히는 현상을 초래한다.
  • 두 라인이 서로 상대방의 자원을 가지고 방출하지 않으면 자물쇠가 사라진다
  • 세립도 자물쇠를 사용하면 자물쇠가 사라질 수 있음
  • 만약에 고객이 카운터 직원인 장삼을 찾아 이체 업무를 한다. 계좌 A이체 계좌 B가 100원이다. 이때 다른 고객이 카운터 직원인 이사도 이체 업무를 한다. 계좌 B이체 계좌 A가 100원이다. 그래서 장삼과 이사는 동시에 서류함에 가서 장부를 받는다. 이때 공교롭게도 장삼이 장부 A를 받았고 이사가 장부 B를 받았다.장삼은 장부 A를 받으면 장부 B(장부 B는 이미 이사가 가져갔다)를 기다리고, 이사는 장부 B를 받으면 장부 A(장부 A는 이미 장삼이 가져갔다)를 기다린다. 얼마나 기다려야 할까?그들은 영원히 기다릴 것이다... 왜냐하면 장삼은 장부 A를 돌려보내지 않을 것이고, 이사도 장부 B를 돌려보내지 않을 것이다.우리 우선 사등이라고 합시다.
    어떻게 하면 자물쇠가 사라지는 것을 피할 수 있습니까
  • 서로 배척하고 공유 자원 X와 Y는 한 라인에만 점용된다.
  • 점유하고 기다린다. 스레드 T1은 공유 자원 X를 획득했고 공유 자원 Y를 기다릴 때 공유 자원 x를 방출하지 않는다.
  • 선점할 수 없고 다른 라인은 라인 T1이 점유한 자원을 강제로 선점할 수 없다.
  • 순환 대기, 라인 1 대기 라인 T2가 차지하는 자원, 라인 T2 대기 라인 T1이 차지하는 자원, 바로 순환 대기입니다.

  • 그중의 하나만 파괴하면 자물쇠가 사라지는 것을 피할 수 있다

    대기 - 알림 메커니즘


    synchronized로 대기 - 알림 메커니즘 구현
  • synchronized와wait(), notif(), notifyAll() 세 가지 방법은 쉽게 실현할 수 있다.
  • wait(): 현재 스레드 방출 자물쇠, 막힌 상태 진입
  • notif(), notifAll(): 막힌 라인을 계속 실행할 수 있음을 알리고 라인이 실행 가능한 상태로 들어갑니다
  • notif()는 대기대에 나쁜 라인을 무작위로 알린다
  • notifyAll () 는 대기 대기열의 모든 라인을 알립니다. notifAll ()
  • 을 사용하십시오.
    wait와 sleep의 차이점:
    sleep은 Object의 메서드이고 wait는 Thread의 메서드입니다.
    wait 자물쇠 방출,sleep 자물쇠 방출
    wait는 notif로 깨우고, sleep로 시간을 설정해야 합니다. 시간이 되면 깨우기
    wait 이상 포획 필요 없음,sleep 필요
    wait (): 현재 스레드가 막혔습니다
    ****문자가 쉽지 않으니 도움이 되시면 팔로우 주세요****
    ****애기술애생활QQ군:894109590***

    좋은 웹페이지 즐겨찾기