같은 클래스에서 두 개의 메소드를 동기화하면 동시에 실행할 수 있습니까?


164

같은 클래스에서 두 개의 메소드를 동기화 하면 동일한 객체 에서 동시에 실행할 수 있습니까? 예를 들면 다음과 같습니다.

class A {
    public synchronized void methodA() {
        //method A
    }

    public synchronized void methodB() {
        // method B
    }
}

methodA()두 개의 다른 스레드에서 동일한 객체에서 두 번 실행할 수 없다는 것을 알고 있습니다 . 에서 같은 것 methodB().

그러나 여전히 실행중인 methodB()동안 다른 스레드에서 실행할 수 methodA()있습니까? (같은 객체)

답변:


148

두 방법 모두 같은 모니터를 잠급니다. 따라서 다른 스레드의 동일한 객체에서 동시에 실행할 수 없습니다 (두 방법 중 하나는 다른 방법이 완료 될 때까지 차단됨).


1
나는이 질문에 추가했다. 두 메소드가 모두 정적이라고 가정하면 methodA는 Class를 사용하여 호출되고 methodB는 t1의 A.methodA () 및 t2의 obj.methodB ()와 같은 객체를 사용하여 호출됩니다. 이제 무슨 일이 일어날까요?
amod

2
@ amod0017 : obj.methodB()동의어 인 A.methodB()경우 methodB()입니다 static. 따라서 예, 그들은 (객체가 아닌 클래스의 모니터) 차단합니다.
NPE

시도하고 다시 돌아올 것입니다. :)
amod

@NPE 따라서 두 메소드가 정적이며 동일한 객체에서 2 개의 스레드 t1 및 t2가 methodA () 및 methodB ()를 동시에 호출하려고 시도하더라도 1 (t1) 스레드 만 실행되고 다른 스레드는 t1이 잠금을 해제 할 때까지 기다려야합니다 ?
sreeprasad

8
정적 메서드는 .class객체에 잠금을 사용 합니다. 당신이 가지고 있다면 class A {static synchronized void m() {} }. 그런 다음 하나의 스레드가 객체 new A().m()잠금을 획득 new A()합니다. 만약 다른 스레드가 호출 A.m()하면, 스레드는 NO 잠금 이 이런 종류의 잠금을 가지고 있지 않기 때문에 찾고자 하는 것은 A.class객체 에 대한 잠금이기 때문에 METHOD NO NOBLEM 을 입력 합니다 . 따라서 메소드를 선언했지만 실제로 는 같은 시간 에 두 개의 다른 스레드에 의해 액세스 됩니다 . 따라서 : 정적 메서드를 호출하기 위해 객체 참조를 사용하지 마십시오synchronized
Alex Semeniuk

113

예제에서 methodA와 methodB는 인스턴스 메소드입니다 (정적 메소드와 반대). 퍼팅 synchronized스레드가 상기 방법은 스레드가 그 방법에서 코드를 실행을 시작하기 전에 호출되는 오브젝트 인스턴스에 대한 잠금 (이하 "극한 로크")를 획득한다는 것을 인스턴스 메소드 수단에.

동기화 된 것으로 표시된 두 개의 다른 인스턴스 메소드가 있고 다른 스레드가 동일한 오브젝트에서 해당 메소드를 동시에 호출하는 경우 해당 스레드는 동일한 잠금을 위해 경합합니다. 한 스레드가 잠금을 획득하면 다른 모든 스레드는 해당 오브젝트의 모든 동기화 된 인스턴스 메소드에서 종료됩니다.

두 방법을 동시에 실행하려면 다음과 같이 다른 잠금을 사용해야합니다.

class A {
    private final Object lockA = new Object();
    private final Object lockB = new Object();

    public void methodA() {
        synchronized(lockA) {
            //method A
        }
    }

    public void methodB() {
        synchronized(lockB) {
            //method B
        }
    }
}

여기서 동기화 된 블록 구문은 실행 스레드가 블록에 들어가기 위해 본질적 잠금을 획득해야하는 특정 객체를 지정할 수있게합니다.

이해해야 할 중요한 점은 개별 메소드에 "동기화 된"키워드를 사용하더라도 핵심 개념은 장면 뒤의 본질적인 잠금입니다.

다음은 Java 학습서 가 관계를 설명하는 방법입니다 .

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

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

잠금의 목적은 공유 데이터를 보호하는 것입니다. 위의 예제 코드에 표시된대로 각 잠금이 서로 다른 데이터 멤버를 보호하는 경우에만 별도의 잠금을 사용합니다.


따라서이 예제에서 잠금은 클래스 A가 아닌 lockA \ lockB 객체에 있습니까? 이것은 클래스 수준 잠금 예입니까?
님로드

2
@ Nimrod : A의 인스턴스가 아닌 lockA 및 lockB 객체에 잠겨 있습니다. 여기에 아무것도 클래스에 잠겨 있지 않습니다. 클래스 수준의 잠금 뭔가 같은 사용하여 클래스 객체에 대한 잠금을 얻기 의미 static synchronizedsynchronized (A.class)
나단 휴즈

여기에 답변 된 내용을 정확하게 설명하는 Java 자습서 링크 가 있습니다.
Alberto de Paola

18

Java Thread는 인스턴스 동기화 Java 메소드에 들어갈 때 오브젝트 레벨 잠금을 획득하고 정적 동기화 Java 메소드에 들어갈 때 클래스 레벨 잠금을 획득합니다 .

귀하의 경우, 메소드 (인스턴스)는 동일한 클래스입니다. 따라서 스레드가 Java 동기화 메소드 또는 블록에 들어가면 잠금 (메소드가 호출되는 객체)을 얻습니다. 따라서 첫 번째 메소드가 완료되고 오브젝트에서 잠금이 해제 될 때까지 동일한 오브젝트에서 다른 메소드를 동시에 호출 할 수 없습니다.


클래스의 두 개의 다른 인스턴스에 두 개의 스레드가있는 경우 두 스레드를 동시에 실행하여 한 스레드가 하나의 동기화 된 메소드를 호출하고 다른 하나는 두 번째 동기화 된 메소드를 호출 할 수 있습니다. 내 이해가 정확 private final Object lock = new object();하면 하나의 스레드 만 메소드 중 하나를 실행할 수 있도록 동기화와 함께 사용할 수 있습니까? 감사합니다
Yug Singh

13

귀하의 경우 동일한 클래스 인스턴스에서 두 개의 메소드를 동기화했습니다. 따라서이 두 가지 방법은 동일한 클래스 A 인스턴스의 다른 스레드에서 동시에 실행할 수 없습니다. 그러나 다른 클래스 A 인스턴스에서는 가능합니다.

class A {
    public synchronized void methodA() {
        //method A
    }
}

와 같다:

class A {
    public void methodA() {
        synchronized(this){
            // code of method A
        }
    }
}

잠금을 정의 private final Object lock = new Object();하고 이제 lock두 가지 방법으로 동기화 블록과 함께 사용 하면 명령문이 적용됩니까? IMO는 Object가 모든 객체의 부모 클래스이므로 스레드가 클래스의 다른 인스턴스에 있더라도 한 번에 하나의 동기화 된 블록 내부의 코드에 액세스 할 수 있습니다. 감사.
Yug Singh

클래스에서 "private final Object lock"을 정의하고 동기화하면 클래스 인스턴스 당 잠금이 있으므로 synchronized (this)와 동일하게 동작합니다.
Oleksandr_DJ

예, Object는 모든 클래스의 부모이지만 귀하의 경우 "잠금"인스턴스는 "소유 클래스 당 인스턴스"이므로 동기화에 "this"와 동일한 효과가 있습니다.
Oleksandr_DJ

7

오라클 문서 링크에서

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

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

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

이것은 당신의 질문에 대답 할 것입니다 : 같은 객체에서, 첫 번째 동기화 된 메소드 실행이 진행 중일 때 두 번째 동기화 된 메소드를 호출 할 수 없습니다.

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


6

코드를 아래 코드로 생각하십시오.

class A {

public void methodA() {
    synchronized(this){        
      //method A body
    }
}

public void methodB() {
    synchronized(this){
      // method B body
    }
}

따라서 메소드 레벨에서 동기화는 단순히 동기화 됨 (this)을 의미합니다. 스레드가이 클래스의 메소드를 실행하는 경우 실행을 시작하기 전에 잠금을 확보하고 메소드 실행이 완료 될 때까지 보유합니다.

그러나 methodA ()가 계속 실행되는 동안 다른 스레드에서 methodB ()를 실행할 수 있습니까? (같은 객체)

실제로는 불가능합니다!

따라서 여러 스레드가 동일한 객체에서 동시에 여러 개의 동기화 된 메소드를 실행할 수 없습니다.


같은 클래스의 서로 다른 두 객체에 스레드를 만들면 어떻게됩니까? 이 경우 한 스레드에서 하나의 메소드를 호출하고 두 번째 스레드에서 다른 메소드를 호출하면 동시에 실행되지 않습니까?
Yug Singh

2
그것들은 다른 대상이기 때문입니다. 즉,이를 방지하려면 정적 메소드를 사용하고 클래스를 동기화하거나 클래스 변수 오브젝트를 잠금으로 사용하거나 클래스를 싱글 톤으로 만들 수 있습니다. @ Yug Singh
Khosro Makari

4

명확하게 말하면 정적 동기화 및 비 정적 동기화 방법은 객체 수준 잠금 및 다른 클래스 수준 잠금이 있기 때문에 동시에 또는 동시에 실행될 수 있습니다.


3

쉽게 싱크되지 않는 동기화 의 핵심 아이디어는 동일한 객체 인스턴스 에서 메소드가 호출 된 경우에만 효과가 있다는 것입니다.

아래 샘플 프로그램은 똑같이 정확하게 지적하는 것입니다.

public class Test {

public synchronized void methodA(String currentObjectName) throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA out");
}

public synchronized void methodB(String currentObjectName)  throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB out");
}

public static void main(String[] args){
    Test object1 = new Test();
    Test object2 = new Test();
    //passing object instances to the runnable to make calls later
    TestRunner runner = new TestRunner(object1,object2);
    // you need to start atleast two threads to properly see the behaviour
    Thread thread1 = new Thread(runner);
    thread1.start();
    Thread thread2 = new Thread(runner);
    thread2.start();
}
}

class TestRunner implements Runnable {
Test object1;
Test object2;

public TestRunner(Test h1,Test h2) {
    this.object1 = h1;
    this.object2 = h2;
}

@Override
public void run() {
    synchronizedEffectiveAsMethodsCalledOnSameObject(object1);
    //noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(object1,object2);
}

// this method calls the method A and B with same object instance object1 hence simultaneous NOT possible
private void synchronizedEffectiveAsMethodsCalledOnSameObject(Test object1) {
    try {
        object1.methodA("object1");
        object1.methodB("object1");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

// this method calls the method A and B with different object instances object1 and object2 hence simultaneous IS possible
private void noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(Test object1,Test object2) {
    try {
        object1.methodA("object1");
        object2.methodB("object2");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

메소드가 다른 오브젝트 인스턴스에서 호출 되는 경우 동시 액세스가 허용되는 방식의 출력 차이를 확인하십시오.

OUPUT와 noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () 주석 -THE 출력 methodB 아웃>에 methodA 아웃 .. methodB>의 순서이다 methodA * noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () *로 주석 처리됨

synchronizedEffectiveAsMethodsCalledOnSameObject () 주석이 있는 출력-강조 표시된 섹션에서 Thread1 및 Thread0에 의한 메소드 A의 동시 액세스가 출력됩니다.

* synchronizedEffectiveAsMethodsCalledOnSameObject () *로 주석 처리됨

스레드 수를 늘리면 훨씬 더 눈에 띄게됩니다.


2

가능하지 않다면 가능하다면 두 방법 모두 동일한 변수를 동시에 업데이트하여 데이터를 쉽게 손상시킬 수 있습니다.


2

예, 두 스레드를 동시에 실행할 수 있습니다. 각 객체에 하나의 잠금 만 포함되고 모든 동기화 된 메서드에는 잠금이 필요하므로 클래스의 객체를 2 개 생성하는 경우. 따라서 동시에 실행하려면 두 개의 오브젝트를 작성한 후 해당 오브젝트 참조를 사용하여 실행하십시오.


1

클래스가 아닌 객체에서 동기화하고 있습니다. 그래서 그들은 같은 객체에서 동시에 실행할 수 없습니다


0

단일 객체에서 공통 동기화 방법을 실행하는 두 개의 다른 스레드는 객체가 동일하기 때문에 하나의 스레드가 동기화 된 메소드와 함께 사용하면 잠금을 수정해야합니다. 잠금이 활성화되면이 스레드는 대기 상태가됩니다. 잠금이 비활성화 된 경우 객체에 액세스 할 수 있지만 액세스하는 동안 잠금을 활성화하고 실행이 완료된 경우에만 잠금을 해제합니다. 다른 스레드가 도착하면 잠금을 수정합니다. 활성화되었으므로 첫 번째 스레드가 실행을 완료하고 객체에 대한 잠금을 해제 할 때까지 기다립니다. 잠금이 해제되면 두 번째 스레드가 객체에 액세스 할 수 있습니다. 실행될 때까지 잠금을 활성화합니다. 따라서 실행이 동시에 이루어지지 않으며 두 스레드가 하나씩 실행됩니다.


1
이 엉망진창을 잘 활용하고 대문자를 사용하십시오. 'varify'와 같은 단어는 없습니다.
Lorne의 후작
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.