같은 클래스에 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 ()
. 경우 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 ™ 학습서”에서 :
첫째, 동일한 객체 에서 동기화 된 메소드를 두 번 호출 하여 인터리브 할 수 없습니다. 하나의 스레드가 객체에 대해 동기화 된 메소드를 실행하는 경우 첫 번째 스레드가 객체와 함께 완료 될 때까지 동일한 객체 블록에 대해 동기화 된 메소드를 호출하는 다른 모든 스레드 (일시 중단).
동기화 된 블록의 “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++;
}
}
잠금을 획득해야하는 객체를 지정할 수 있습니다.
포함 객체를 잠그지 않으려면 다음 중에서 선택할 수 있습니다.
- 다른 잠금을 지정하는 동기화 된 블록 사용
- a와 b를 원자로 만들기 (java.util.concurrent.atomic 사용)
답변
오라클 문서에서 링크에서
메소드를 동기화하면 두 가지 효과가 있습니다.
첫째, 동일한 객체에서 동기화 된 메소드를 두 번 호출하여 인터리브 할 수 없습니다. 하나의 스레드가 객체에 대해 동기화 된 메소드를 실행하는 경우 첫 번째 스레드가 객체와 함께 완료 될 때까지 동일한 객체 블록에 대해 동기화 된 메소드를 호출하는 다른 모든 스레드 (일시 중단).
둘째, 동기화 된 메소드가 종료되면 동일한 오브젝트에 대한 동기화 된 메소드의 후속 호출과의 사전 관계를 자동으로 설정합니다. 이를 통해 객체 상태의 변경 사항이 모든 스레드에 표시됩니다.
내장 잠금 및 잠금 동작을 이해하려면 이 설명서 페이지 를 참조하십시오 .
이것은 귀하의 질문에 대답 할 것입니다 : 동일한 객체 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에 들어갈 수 있습니다.
답변
다음과 같은 것을 할 수 있습니다. 이 경우 “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++;
}
}
}