Programing

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

lottogame 2020. 5. 23. 08:53
반응형

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


같은 클래스에 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()같은 시간에?


메소드를 동기화 된 것으로 선언하면 (을 입력하여 수행하는 것처럼 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();
    }
}

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

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

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

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

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


동기화 된 메소드의 "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++;
        }
    }
}

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

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

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

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

If you want to avoid locking on the containing object you can choose between:


From oracle documentation link

Making methods synchronized has two effects:

First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object.

Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads

Have a look at this documentation page to understand intrinsic locks and lock behavior.

This will answer your question: On same object x , you can't call x.addA() and x.addB() at same time when one of the synchronized methods execution is in progress.


You can do something like the following. In this case you are using the lock on a and b to synchronized instead of the lock on "this". We cannot use int because primitive values don't have locks, so we use Integer.

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

If you have some methods which are not synchronized and are accessing and changing the instance variables. In your example:

 private int a;
 private int b;

any number of threads can access these non synchronized methods at the same time when other thread is in the synchronized method of the same object and can make changes to instance variables. For e.g :-

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

You need to avoid the scenario that non synchronized methods are accessing the instance variables and changing it otherwise there is no point of using synchronized methods.

In the below scenario:-

class X {

        private int a;
        private int b;

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

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

Only one of the threads can be either in addA or addB method but at the same time any number of threads can enter changeState method. No two threads can enter addA and addB at same time(because of Object level locking) but at same time any number of threads can enter changeState.


This example (although not pretty one) can provide more insight into locking mechanism. If incrementA is synchronized, and incrementB is not synchronized, then incrementB will be executed ASAP, but if incrementB is also synchronized then it has to 'wait' for incrementA to finish, before incrementB can do its job.

Both methods are called onto single instance - object, in this example it is: job, and 'competing' threads are aThread and main.

Try with 'synchronized' in incrementB and without it and you will see different results.If incrementB is 'synchronized' as well then it has to wait for incrementA() to finish. Run several times each variant.

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

Yes, it will block the other method because synchronized method applies to the WHOLE class object as pointed .... but anyway it will block the other thread execution ONLY while performing the sum in whatever method addA or addB it enters, because when it finish ... the one thread will FREE the object and the other thread will access the other method and so on perfectly working.

I mean the "synchronized" is made precisely for blocking the other thread from accessing another while in a specific code execution. SO FINALLY THIS CODE WILL WORK FINE.

As a final note, if there is an 'a' and 'b' variables, not just an unique variable 'a' or whatever other name, there is no need to synchronize this methods cause it is perfectly safe accesing other var (Other memory location).

class X {

private int a;
private int b;

public void addA(){
    a++;
}

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

Will work as well


This might not work as the boxing and autoboxing from Integer to int and viceversa is dependant on JVM and there is high possibility that two different numbers might get hashed to same address if they are between -128 and 127.


In java synchronization,if a thread want to enter into synchronization method it will acquire lock on all synchronized methods of that object not just on one synchronized method that thread is using. So a thread executing addA() will acquire lock on addA() and addB() as both are synchronized.So other threads with same object cannot execute addB().

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

반응형