[java] Synchronized Block 대신 Synchronized Method를 사용하는 것이 유리합니까?

예를 들어 동기화 된 블록보다 동기화 된 방법의 장점을 알려주는 사람이 있습니까?



답변

누구든지 예를 들어 동기화 된 블록보다 동기화 된 방법의 장점을 말해 줄 수 있습니까? 감사.

블록보다 동기화 된 방법을 사용하면 확실한 이점이 없습니다.

아마도 유일한 것은 (그러나 나는 그것을 이점이라고 부르지 않을 것입니다) 당신은 객체 참조를 포함 할 필요가 없다는 것 this입니다.

방법:

public synchronized void method() { // blocks "this" from here.... 
    ...
    ...
    ...
} // to here

블록:

public void method() {
    synchronized( this ) { // blocks "this" from here .... 
        ....
        ....
        ....
    }  // to here...
}

보다? 전혀 이점이 없습니다.

블록 다른 객체를 잠금으로 사용할 수 있지만 메서드를 동기화하면 전체 객체가 잠길 수 있기 때문에 대부분의 유연성에서 블록 메서드보다 장점이 있습니다.

비교:

// locks the whole object
...
private synchronized void someInputRelatedWork() {
    ...
}
private synchronized void someOutputRelatedWork() {
    ...
}

vs.

// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();

private void someInputRelatedWork() {
    synchronized(inputLock) {
        ...
    }
}
private void someOutputRelatedWork() {
    synchronized(outputLock) {
        ...
    }
}

또한 방법이 커지면 동기화 된 섹션을 계속 분리 할 수 ​​있습니다.

 private void method() {
     ... code here
     ... code here
     ... code here
    synchronized( lock ) {
        ... very few lines of code here
    }
     ... code here
     ... code here
     ... code here
     ... code here
}


답변

유일한 차이점은 동기화 된 블록이 동기화 할 객체를 선택할 수 있다는 것입니다. 동기화 된 메소드는 'this'(또는 동기화 된 클래스 메소드의 해당 클래스 인스턴스) 만 사용할 수 있습니다 . 예를 들어 다음은 의미 상 동일합니다.

synchronized void foo() {
  ...
}

void foo() {
    synchronized (this) {
      ...
    }
}

후자는 모든 객체, 종종 멤버 변수 의 관련 잠금을 위해 경쟁 할 수 있기 때문에 더 유연 합니다. 또한 블록 전후에 동시에 메서드 내에서 동시 코드를 실행할 수 있기 때문에 더 세분화됩니다. 물론 동시 코드를 별도의 비 동기화 메소드로 리팩토링하여 동기화 된 메소드를 쉽게 사용할 수 있습니다. 코드를 이해하기 쉽게 만드는 방법을 사용하십시오.


답변

동기화 된 방법

장점 :

  • IDE는 동기화 된 메소드를 표시 할 수 있습니다.
  • 구문이 더 간결합니다.
  • 동기화 된 블록을 분리하여 메소드를 분리합니다.

단점 :

  • 이것과 동기화되므로 외부인도 동기화 할 수 있습니다.
  • 동기화 된 블록 외부로 코드를 이동하기가 더 어렵습니다.

동기화 된 블록

장점 :

  • 잠금에 개인 변수를 사용하여 잠금을 클래스 내부에 유지하도록 허용합니다.
  • 변수에 대한 참조를 검색하여 동기화 된 블록을 찾을 수 있습니다.

단점 :

  • 구문이 더 복잡하므로 코드를 읽기가 어렵습니다.

개인적으로 동기화가 필요한 것에 중점을 둔 클래스와 동기화 된 메소드를 사용하는 것을 선호합니다. 이러한 클래스는 가능한 한 작아야하므로 동기화를 쉽게 검토 할 수 있어야합니다. 다른 사람들은 동기화에 신경 쓸 필요가 없습니다.


답변

주요 차이점은 동기화 된 블록을 사용하는 경우이 이외의 다른 객체를 잠그면 훨씬 더 유연해질 수 있다는 것입니다.

메시지 대기열과 여러 메시지 생산자 및 소비자가 있다고 가정합니다. 우리는 제작자가 서로 간섭하는 것을 원하지 않지만 소비자는 제작자를 기다릴 필요없이 메시지를 검색 할 수 있어야합니다. 그래서 우리는 단지 객체를 생성합니다

Object writeLock = new Object();

그리고 지금부터 프로듀서는 새 메시지를 추가하려고 할 때마다 다음과 같이 잠급니다.

synchronized(writeLock){
  // do something
}

따라서 소비자는 여전히 읽을 수 있으며 생산자는 잠겨 있습니다.


답변

동기화 된 방법

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

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

생성자를 동기화 할 수는 없습니다. 생성자와 동기화 된 키워드를 사용하면 구문 오류가 발생합니다. 생성자를 동기화하는 것은 의미가 없습니다. 객체를 생성하는 스레드 만 생성되는 동안 액세스 할 수 있어야하기 때문입니다.

동기화 된 진술

동기화 된 메소드와 달리, 동기화 된 명령문은 고유 잠금을 제공하는 오브젝트를 지정해야합니다. 대부분은 목록 또는 맵에 대한 액세스를 동기화하기 위해이를 사용하지만 오브젝트의 모든 메소드에 대한 액세스를 차단하고 싶지는 않습니다.

Q : 내장 잠금 및 동기화 동기화는 내장 잠금 또는 모니터 잠금이라고하는 내부 엔터티를 중심으로 구축됩니다. (API 사양은 종종이 엔티티를 단순히 “모니터”라고합니다.) 내장 잠금은 동기화의 두 측면에서 중요한 역할을합니다. 즉 객체 상태에 대한 독점 액세스를 강화하고 가시성에 필수적인 관계를 설정합니다.

모든 객체에는 관련된 고유 잠금이 있습니다. 일반적으로 객체 필드에 독점적이고 일관된 액세스가 필요한 스레드는 객체에 액세스하기 전에 객체의 본질적 잠금을 획득 한 다음 완료되면 본질적 잠금을 해제해야합니다. 스레드는 잠금을 획득하고 잠금을 해제 한 시간 사이에 고유 잠금을 소유한다고합니다. 스레드가 내장 잠금을 소유하는 한 다른 스레드는 동일한 잠금을 얻을 수 없습니다. 다른 스레드는 잠금을 획득하려고 시도 할 때 차단됩니다.

package test;

public class SynchTest implements Runnable {
    private int c = 0;

    public static void main(String[] args) {
        new SynchTest().test();
    }

    public void test() {
        // Create the object with the run() method
        Runnable runnable = new SynchTest();
        Runnable runnable2 = new SynchTest();
        // Create the thread supplying it with the runnable object
        Thread thread = new Thread(runnable,"thread-1");
        Thread thread2 = new Thread(runnable,"thread-2");
//      Here the key point is passing same object, if you pass runnable2 for thread2,
//      then its not applicable for synchronization test and that wont give expected
//      output Synchronization method means "it is not possible for two invocations
//      of synchronized methods on the same object to interleave"

        // Start the thread
        thread.start();
        thread2.start();
    }

    public synchronized  void increment() {
        System.out.println("Begin thread " + Thread.currentThread().getName());
        System.out.println(this.hashCode() + "Value of C = " + c);
//      If we uncomment this for synchronized block, then the result would be different
//      synchronized(this) {
            for (int i = 0; i < 9999999; i++) {
                c += i;
            }
//      }
        System.out.println("End thread " + Thread.currentThread().getName());
    }

//    public synchronized void decrement() {
//        System.out.println("Decrement " + Thread.currentThread().getName());
//    }

    public int value() {
        return c;
    }

    @Override
    public void run() {
        this.increment();
    }
}

동기화 된 방법, 블록 및 동기화없이 서로 다른 출력을 교차 점검합니다.


답변

참고 : 정적 동기화 된 메소드 및 블록은 클래스 오브젝트에서 작동합니다.

public class MyClass {
   // locks MyClass.class
   public static synchronized void foo() {
// do something
   }

   // similar
   public static void foo() {
      synchronized(MyClass.class) {
// do something
      }
   }
}


답변

Java 컴파일러가 소스 코드를 바이트 코드로 변환하면 동기화 된 메소드와 동기화 된 블록을 매우 다르게 처리합니다.

JVM이 동기화 된 메소드를 실행할 때 실행중인 스레드는 메소드의 method_info 구조에 ACC_SYNCHRONIZED 플래그가 설정되어 있음을 식별 한 후 자동으로 오브젝트의 잠금을 획득하고 메소드를 호출 한 후 잠금을 해제합니다. 예외가 발생하면 스레드가 자동으로 잠금을 해제합니다.

반면에 메소드 블록 동기화는 오브젝트의 잠금 및 예외 처리를 확보하기위한 JVM의 내장 지원을 생략하며 기능을 명시 적으로 바이트 코드로 작성해야합니다. 동기화 된 블록이있는 메소드의 바이트 코드를 읽으면이 기능을 관리하기위한 12 가지 이상의 추가 작업이 표시됩니다.

다음은 동기화 된 메소드와 동기화 된 블록을 생성하기위한 호출을 보여줍니다.

public class SynchronizationExample {
    private int i;

    public synchronized int synchronizedMethodGet() {
        return i;
    }

    public int synchronizedBlockGet() {
        synchronized( this ) {
            return i;
        }
    }
}

synchronizedMethodGet()메소드는 다음 바이트 코드를 생성합니다.

0:  aload_0
1:  getfield
2:  nop
3:  iconst_m1
4:  ireturn

그리고 synchronizedBlockGet()메소드 의 바이트 코드는 다음과 같습니다.

0:  aload_0
1:  dup
2:  astore_1
3:  monitorenter
4:  aload_0
5:  getfield
6:  nop
7:  iconst_m1
8:  aload_1
9:  monitorexit
10: ireturn
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow

동기화 된 방법과 블록의 한 가지 중요한 차이점은 동기화 된 블록은 일반적으로 잠금 범위를 줄입니다. 잠금 범위는 성능에 반비례하기 때문에 항상 중요한 코드 섹션 만 잠그는 것이 좋습니다. 동기화 된 블록을 사용하는 가장 좋은 예 중 하나는 전체를 잠그는 대신 싱글 톤 패턴의 이중 검사 잠금입니다.getInstance() 방법 Singleton 인스턴스를 만드는 데 사용되는 중요한 코드 섹션 만 잠급니다. 잠금은 한두 번만 필요하므로 성능이 크게 향상됩니다.

동기화 된 메소드를 사용하는 동안 정적 동기화 메소드와 비 정적 동기화 메소드를 모두 혼합하는 경우 특별한주의가 필요합니다.