객체 또는 메소드의 Java 동기화 메소드 잠금?


191

같은 클래스에 2 개의 동기화 된 메소드가 있지만 각각 다른 변수에 액세스하는 경우 2 개의 스레드가 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()같은 시간에?

답변:


199

메소드를 동기화 된 것으로 선언하면 (을 입력하여 수행하는 것처럼 public synchronized void addA()) 전체 객체 하므로 동일한 객체와 다른 변수에 액세스하는 두 스레드가 서로를 차단합니다.

한 번에 하나의 변수 만 동기화하려는 경우 다른 변수에 액세스하는 동안 두 스레드가 서로를 차단하지 않으면 블록에서 개별적으로 동기화됩니다 synchronized (). 경우 ab객체 참조했다 당신은 사용합니다 :

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();
    }
}

181
메소드를 동기화하면 전체 객체를 잠그므로 동일한 객체와 다른 변수에 액세스하는 두 스레드가 서로 차단됩니다. 약간 오해의 소지가 있습니다. 방법에 대한 동기화는 기능적으로 방법 synchronized (this)의 본문 주위 에 블록 을 갖는 것과 같습니다 . "this"개체는 잠기지 않고 "this"개체는 뮤텍스로 사용되며 본문은 "this"에 동기화 된 다른 코드 섹션과 동시에 실행되지 않습니다. 동기화되지 않은 "this"의 다른 필드 / 방법에는 영향을 미치지 않습니다.
Mark Peters

13
그렇습니다. 실제 예를 들어 - - 이것 좀 봐 stackoverflow.com/questions/14447095/... - 요약 : 잠금은 동기화 방법의 수준과 객체의 인스턴스 변수에있다가 다른 스레드에서 액세스 할 수 있습니다

5
첫 번째 예는 근본적으로 깨졌습니다. 경우 ab예를 들어 개체였다 Integer의, 당신은 당신이하는 경우에 동기화 된 다른 개체와 대체 적용 할 때 ++연산자를.
Holger

답을 고치고 AtomicInteger를 초기화하십시오 : AtomicInteger a = new AtomicInteger (0);
Mehdi

어쩌면이 anwser는 객체 자체의 동기화에 대한 다른 설명에서 업데이트되어야합니다. stackoverflow.com/a/10324280/1099452
lucasvc

71

메소드 선언에 동기화 된 것은이를위한 구문 설탕입니다.

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

정적 방법에서는 다음과 같이 구문 설탕입니다.

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

Java 디자이너가 동기화에 대해 지금 이해하고있는 것을 알고 있다면, 동시성 구현이 잘못되는 경우가 많기 때문에 구문 설탕을 추가하지 않았을 것입니다.


3
사실이 아니다. synchronized 메소드는 synchronized (object)와 다른 바이트 코드를 생성합니다. 기능은 동일하지만 구문 설탕 이상입니다.
Steve Kuo 2016 년

10
"구문 설탕"이 바이트 코드와 동등한 것으로 엄격하게 정의되어 있다고 생각하지 않습니다. 요점은 기능적으로 동일하다는 것입니다.
Yishai

1
자바 디자이너가 모니터에 대해 이미 알려진 것을 알고 있다면 기본적으로 유닉스 내부를 모방하는 대신 다르게 ​​수행했을 것입니다. 브린 치 한센 (Brinch Hansen)은 자바 동시성 프리미티브를보고 '나는 분명히 헛된 노력을했다'고 말했다 .
Lorne의 후작

사실입니다. OP가 제공 한 예는 각 방법을 잠그는 것처럼 보이지만 실제로는 모두 동일한 객체에서 잠 깁니다. 매우기만적인 구문. 10 년 이상 Java를 사용한 후 나는 이것을 몰랐습니다. 그래서 나는 이런 이유로 동기화 된 메소드를 피할 것입니다. 나는 항상 동기화 된 것으로 정의 된 각 메소드에 대해 보이지 않는 객체가 생성되었다고 생각했습니다.
Peter Quiring

21

동기화 된 메소드의 "Java ™ 학습서"에서 :

첫째, 동일한 객체 에서 동기화 된 메소드를 두 번 호출 하여 인터리브 할 수 없습니다. 하나의 스레드가 객체에 대해 동기화 된 메소드를 실행하는 경우 첫 번째 스레드가 객체와 함께 완료 될 때까지 동일한 객체 블록에 대해 동기화 된 메소드를 호출하는 다른 모든 스레드 (일시 중단).

동기화 된 블록의 "The Java ™ Tutorials"에서 :

동기화 된 명령문은 또한 세분화 된 동기화로 동시성을 개선하는 데 유용합니다. 예를 들어, MsLunch 클래스에 결코 사용되지 않는 두 개의 인스턴스 필드 c1 및 c2가 있다고 가정하십시오. 이러한 필드의 모든 업데이트는 동기화되어야 하지만 c1 업데이트가 c2 업데이트에 인터리브되는 것을 막을 이유가 없습니다. 그렇게하면 불필요한 차단을 만들어 동시성을 줄일 수 있습니다. 동기화 된 메소드를 사용하거나 이와 관련된 잠금을 사용하는 대신 잠금을 제공하기 위해 두 개의 객체를 만듭니다.

(엠파 시스 마인)

2 개의 비 인터리빙 변수 가 있다고 가정하십시오 . 따라서 다른 스레드에서 동시에 각 스레드에 액세스하려고합니다. 객체 클래스 자체가 아니라 아래와 같이 Object 클래스에 잠금 을 정의해야 합니다 (두 번째 Oracle 링크의 예).

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++;
        }
    }
}

14

액세스 된 잠금은 메소드가 아닌 오브젝트에 있습니다. 메소드 내에서 액세스되는 변수는 관련이 없습니다.

메소드에 "동기화"를 추가하면 코드를 실행하는 스레드가 진행하기 전에 오브젝트에 대한 잠금을 획득해야 함을 의미합니다. "정적 동기화"를 추가하면 코드를 실행하는 스레드가 진행하기 전에 클래스 객체에 대한 잠금을 획득해야 함을 의미합니다. 또는 다음과 같이 블록으로 코드를 감쌀 수 있습니다.

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

잠금을 획득해야하는 객체를 지정할 수 있습니다.

포함 객체를 잠그지 않으려면 다음 중에서 선택할 수 있습니다.


7

오라클 문서에서 링크에서

메소드를 동기화하면 두 가지 효과가 있습니다.

첫째, 동일한 객체에서 동기화 된 메소드를 두 번 호출하여 인터리브 할 수 없습니다. 하나의 스레드가 객체에 대해 동기화 된 메소드를 실행하는 경우 첫 번째 스레드가 객체와 함께 완료 될 때까지 동일한 객체 블록에 대해 동기화 된 메소드를 호출하는 다른 모든 스레드 (일시 중단).

둘째, 동기화 된 메소드가 종료되면 동일한 오브젝트에 대한 동기화 된 메소드의 후속 호출과의 사전 관계를 자동으로 설정합니다. 이를 통해 객체 상태의 변경 사항이 모든 스레드에 표시됩니다.

내장 잠금 및 잠금 동작을 이해하려면 이 설명서 페이지참조하십시오 .

이것은 귀하의 질문에 대답 할 것입니다 : 동일한 객체 x에서 동기화 된 메소드 실행 중 하나가 진행 중일 때 x.addA () 및 x.addB ()를 동시에 호출 할 수 없습니다.


4

동기화되지 않고 인스턴스 변수에 액세스하고 변경하는 메소드가있는 경우 귀하의 예에서 :

 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에 들어갈 수 있습니다.


3

다음과 같은 것을 할 수 있습니다. 이 경우 "this"의 잠금 대신 a 및 b의 잠금을 사용하여 동기화합니다. 기본 값에는 잠금이 없으므로 int를 사용할 수 없으므로 Integer를 사용합니다.

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

3

예, 동기화 된 메소드가 WHOLE 클래스 객체에 지정된대로 적용하기 때문에 다른 메소드를 차단합니다 .... 어쨌든 addA 또는 addB 메소드에서 합계를 수행하는 동안에 다른 스레드 실행을 차단합니다. ... 하나의 스레드는 무료입니다 객체를 다른 스레드는 다른 방법에 액세스 등이 완벽하게 작동합니다.

"동기화"는 특정 코드 실행 중에 다른 스레드가 다른 스레드에 액세스하는 것을 차단하기 위해 정확하게 만들어진다는 것을 의미합니다. 마지막으로이 코드는 제대로 작동합니다.

마지막으로, 고유 변수 'a'또는 다른 이름이 아닌 'a'및 'b'변수가있는 경우이 방법을 동기화 할 필요가 없으므로 다른 var (다른 메모리에 완벽하게 액세스 할 수 있음) 위치).

class X {

private int a;
private int b;

public void addA(){
    a++;
}

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

잘 작동합니다


2

이 예제는 (아주 쉽지는 않지만) 잠금 메커니즘에 대한 더 많은 통찰력을 제공 할 수 있습니다. 경우 incrementA가 되고 동기화incrementB가 되고 동기화되지 , 다음 incrementB는 최대한 빨리 실행되지만, 경우 incrementB는 또한 동기화 그것은을위한 '대기'에있다 incrementA 전에 마무리 incrementB이 그 일을 할 수 있습니다.

두 메소드 모두 단일 인스턴스 오브젝트에서 호출됩니다.이 예제에서는 job 이고 'competing'스레드는 aThreadmain 입니다.

incrementB 에서 ' 동기화 '로 시도 하면 다른 결과가 표시됩니다. incrementB 가 ' 동기화 '인 경우 incrementalA ()가 완료 될 때까지 기다려야합니다 . 각 변형을 여러 번 실행하십시오.

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();
    }
}

1

자바 동기화에서 스레드가 동기화 방법에 들어가기를 원한다면 스레드가 사용하는 하나의 동기화 된 메소드뿐만 아니라 해당 객체의 모든 동기화 된 메소드에 대한 잠금을 얻습니다. 따라서 addA ()를 실행하는 스레드는 모두 동기화 될 때 addA () 및 addB ()에 대한 잠금을 획득하므로 동일한 객체를 가진 다른 스레드는 addB ()를 실행할 수 없습니다.


0

Integer에서 int 로의 boxing 및 autoboxing은 작동하지 않을 수 있으며 그 반대의 경우 JVM에 종속되며 두 개의 다른 숫자가 -128에서 127 사이 인 경우 동일한 주소로 해시 될 가능성이 높습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.