IT이야기

Java 동기화된 메서드 잠금 객체 또는 메서드?

cyworld 2022. 5. 3. 20:57
반응형

Java 동기화된 메서드 잠금 객체 또는 메서드?

동일한 클래스에 동기화된 메서드가 2개 있지만 각 변수가 서로 다른 경우, 두 개의 스레드가 동시에 두 메서드에 액세스할 수 있는가?물체에 잠금이 발생하는가, 아니면 동기화된 방법 내부의 변수만큼 구체적인가?

예:

class X {

    private int a;
    private int b;

    public synchronized void addA(){
        a++;
    }

    public synchronized void addB(){
        b++;
    }

}

2개의 스레드가 클래스 X 수행의 동일한 인스턴스에 액세스할 수 있는지 여부x.addA() 및x.addB()동시에?

메서드를 동기화된 것으로 선언하는 경우(입력하여 수행 중인 경우)public synchronized void addA()) 전체 객체에 동기화하여 이 동일한 객체와 다른 변수에 접근하는 두 개의 스레드가 서로 차단된다.

두 개의 스레드가 서로 다른 변수에 액세스하는 동안 서로 차단되지 않도록 한 번에 한 변수에 대해서만 동기화하려면synchronized ()블록들만약a그리고b사용할 객체 참조:

public void addA() {
    synchronized( a ) {
        a++;
    }
}

public void addB() {
    synchronized( b ) {
        b++;
    }
}

하지만 그것들은 원시적인 것들이기 때문에 당신은 이것을 할 수 없다.

대신 AtomicInteger를 사용해 보십시오.

import java.util.concurrent.atomic.AtomicInteger;

class X {

    AtomicInteger a;
    AtomicInteger b;

    public void addA(){
        a.incrementAndGet();
    }

    public void addB(){ 
        b.incrementAndGet();
    }
}

메소드 선언에 동기화되어 이를 위한 구문 당분:

 public void addA() {
     synchronized (this) {
          a++;
     }
  }

정적 방법에서는 이를 위한 구문 당분이다.

 ClassA {
     public static void addA() {
          synchronized(ClassA.class) {
              a++;
          }
 }

만약 자바 디자이너들이 동기화에 대해 현재 이해되고 있는 것을 그때 알았다면, 그들은 동시성의 나쁜 구현으로 이어지는 경우가 많으므로, 동시성의 설탕을 첨가하지 않았을 것이라고 생각한다.

동기화메서드에 대한 "Java™ 튜토리얼"의 내용:

첫째, 동일한 객체에서 동기화된 메서드의 두 번의 호출이 상호 이탈하는 것은 불가능하다.한 스레드가 개체에 대해 동기화된 메서드를 실행할 때 첫 번째 스레드가 개체와 함께 수행될 때까지 동일한 개체 블록에 대해 동기화된 메서드를 호출하는 다른 모든 스레드(실행 중지)

동기화된 블록의 "Java™ 자습서"에서 다음을 참조하십시오.

동기화된 문장은 세분화된 동기화에 대한 동시성 향상에 유용하다.예를 들어, MsLunch 클래스에 c1과 c2라는 두 개의 인스턴스 필드가 있지만 함께 사용되지 않는다고 가정합시다.이러한 필드의 모든 업데이트는 동기화되어야 하지만 c1의 업데이트가 c2의 업데이트와 인터리빙되는 것을 막을 이유가 없으며, 그렇게 하면 불필요한 차단을 생성하여 동시성을 줄일 수 있다.동기화된 방법을 사용하거나 이와 관련된 잠금을 사용하는 대신, 우리는 잠금을 제공하기 위해 두 개의 객체를 만든다.

(내 것)

두 개의 비 인터리빙 변수가 있다고 가정합시다.따라서 서로 다른 스레드에서 각 스레드에 동시에 액세스하려는 경우.잠금은 오브젝트 클래스 자체가 아니라 아래와 같은 클래스 오브젝트(예: 두 번째 오라클 링크)에서 정의하십시오.

public class MsLunch {

    private long c1 = 0;
    private long c2 = 0;

    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }

    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
}

접근한 자물쇠는 메서드가 아닌 객체에 있다.어떤 변수가 그 방법 안에서 접근되는지는 관련이 없다.

메소드에 "동기화"를 추가하는 것은 코드를 실행하는 스레드가 계속 진행하기 전에 개체의 잠금을 획득해야 함을 의미한다."정적 동기화"를 추가하는 것은 코드를 실행하는 스레드가 계속 진행하기 전에 클래스 객체에 대한 잠금을 획득해야 함을 의미한다.또는 다음과 같은 블록으로 코드를 포장할 수 있다.

public void addA() {
    synchronized(this) {
        a++;
    }
}

잠금을 획득해야 하는 개체를 지정할 수 있도록 하십시오.

포함된 개체의 잠금을 방지하려면 다음 중 하나를 선택하십시오.

Oracle 설명서 링크에서

동기화된 메서드를 만드는 데는 두 가지 효과가 있다.

첫째, 동일한 객체에서 동기화된 메서드의 두 번의 호출이 상호 이탈하는 것은 불가능하다.한 스레드가 개체에 대해 동기화된 메서드를 실행할 때 첫 번째 스레드가 개체와 함께 수행될 때까지 동일한 개체 블록에 대해 동기화된 메서드를 호출하는 다른 모든 스레드(실행 중지)

둘째, 동기화된 방법이 종료되면, 그것은 자동으로 동일한 개체에 대해 동기화된 방법의 후속 호출과 함께 발생 전 관계를 설정한다.이렇게 하면 모든 스레드에 개체의 상태 변경이 표시됨

본질적인 잠금 및 잠금 동작을 이해하려면 이 문서 페이지를 살펴보십시오.

이렇게 하면 다음과 같은 질문에 답할 수 있다.동일한 개체 x 에서 동기화된 메서드 실행 중 하나가 진행 중일 때는 x.addA()와 x.addB()를 동시에 호출할 수 없다.

동기화되지 않고 인스턴스 변수에 액세스하여 변경하는 일부 메서드가 있는 경우예시:

 private int a;
 private int b;

다른 스레드가 동일한 객체의 동기화된 메서드에 있을 때 스레드의 개수는 동시에 이러한 비동기화된 메서드에 액세스할 수 있으며 인스턴스 변수를 변경할 수 있다.예:-

 public void changeState() {
      a++;
      b++;
    }

동기화되지 않은 메서드가 인스턴스 변수에 액세스하고 이를 변경하지 않으면 동기화된 메서드를 사용할 필요가 없다는 시나리오를 피해야 한다.

아래 시나리오에서-

class X {

        private int a;
        private int b;

        public synchronized void addA(){
            a++;
        }

        public synchronized void addB(){
            b++;
        }
     public void changeState() {
          a++;
          b++;
        }
    }

스레드 중 하나만 addA 또는 addB 메서드에 있을 수 있지만 동시에 원하는 수의 스레드만 changeState 메서드에 들어갈 수 있다.(개체 수준 잠금으로 인해) addA와 addB를 동시에 입력할 수 있는 스레드는 없지만, 동시에 임의 수의 스레드가 changeState를 입력할 수 있다.

이 예(예쁘지는 않지만)는 잠금 메커니즘에 대한 더 많은 통찰력을 제공할 수 있다.증분A동기화되고 증분B동기화되지 않으면 증분B가 최대한 빨리 실행되지만 증분B동기화되면 증분A가 완료될 때까지 '대기'해야 증분B가 제 역할을 할 수 있다.

두 방법 모두 단일 인스턴스로 호출된다. 즉, 이 예에서 개체: 작업과 '경쟁' 스레드는 Threadmain이다.

증분B의 '동기화'를 사용하지 않고 '동기화'를 사용해 보십시오. 그러면 다른 결과가 나타날 겁니다.증분B도 '동기화'되면 증분A()가 완료될 때까지 기다려야 한다.각 변종별로 여러 번 실행하십시오.

class LockTest implements Runnable {
    int a = 0;
    int b = 0;

    public synchronized void incrementA() {
        for (int i = 0; i < 100; i++) {
            this.a++;
            System.out.println("Thread: " + Thread.currentThread().getName() + "; a: " + this.a);
        }
    }

    // Try with 'synchronized' and without it and you will see different results
    // if incrementB is 'synchronized' as well then it has to wait for incrementA() to finish

    // public void incrementB() {
    public synchronized void incrementB() {
        this.b++;
        System.out.println("*************** incrementB ********************");
        System.out.println("Thread: " + Thread.currentThread().getName() + "; b: " + this.b);
        System.out.println("*************** incrementB ********************");
    }

    @Override
    public void run() {
        incrementA();
        System.out.println("************ incrementA completed *************");
    }
}

class LockTestMain {
    public static void main(String[] args) throws InterruptedException {
        LockTest job = new LockTest();
        Thread aThread = new Thread(job);
        aThread.setName("aThread");
        aThread.start();
        Thread.sleep(1);
        System.out.println("*************** 'main' calling metod: incrementB **********************");
        job.incrementB();
    }
}

다음과 같은 일을 할 수 있다.이 경우에는 "이것"에 대한 잠금 대신 a와 b의 잠금을 사용하여 동기화하는 것이다.원시 값에는 자물쇠가 없기 때문에 int를 사용할 수 없기 때문에 정수(Int)

class x{
   private Integer a;
   private Integer b;
   public void addA(){
      synchronized(a) {
         a++;
      }
   }
   public synchronized void addB(){
      synchronized(b) {
         b++;
      }
   }
}

그래, 동기화된 메서드가 가리키는 전체 클래스 객체에 적용되기 때문에 다른 메서드를 차단하지만, 어쨌든 다른 스레드 실행은 입력하는 모든 메서드 addA 또는 addB에서 합계를 수행하는 동안만 차단할 것이다. 왜냐하면 한 스레드가 끝나면 다른 스레드는 객체를 자유롭게 하고 다른 스레드는 다른 메토에 액세스하기 때문이다.d 등등 완벽하게 작동한다.

내 말은 "동기화"는 특정 코드를 실행하는 동안 다른 스레드가 다른 스레드에 접근하는 것을 차단하기 위해 정확히 만들어졌다는 것이다.그래서 마침내 이 코드는 잘 작동될거야

마지막 참고 사항으로서, 만약 'a'와 'b' 변수가 있다면, 단순히 'a'와 'b' 변수가 있다면, 다른 변수(기타 메모리 위치)에 완벽하게 적응할 수 있기 때문에 이 방법을 동기화할 필요가 없다.

class X {

private int a;
private int b;

public void addA(){
    a++;
}

public void addB(){
    b++;
}}

역시 효과가 있을 것이다.

자바 동기화에서, 만일 스레드가 동기화 방법에 들어가고자 한다면, 스레드가 사용하고 있는 하나의 동기화된 메서드가 아닌, 그 개체의 모든 동기화된 메서드에 대한 잠금을 획득할 것이다.따라서 addA()를 실행하는 스레드는 둘 다 동기화될 때 addA()와 addB()에 대한 잠금을 획득하게 된다.따라서 동일한 객체를 가진 다른 스레드는 addB()를 실행할 수 없다.

정수에서 내부로 자동 복싱 및 자동 복싱은 JVM에 따라 달라지며 -128에서 127 사이일 경우 두 개의 다른 숫자가 동일한 주소로 해시될 가능성이 높기 때문에 이 방법은 작동하지 않을 수 있다.

참조URL: https://stackoverflow.com/questions/3047564/java-synchronized-method-lock-on-object-or-method

반응형