java - 자바 synchronized




'동기화'란 무엇입니까? (10)

synchronized 키워드 란 무엇입니까?

쓰레드는 주로 필드에 대한 액세스를 공유함으로써 통신하며 객체 참조 필드는 참조합니다. 이러한 형태의 통신은 매우 효율적이지만 스레드 간섭과 메모리 일관성 오류 의 두 가지 종류가 있습니다 . 이러한 오류를 방지하는 데 필요한 도구는 동기화입니다.

동기화 된 블록 또는 메소드는 스레드 간섭을 방지하고 데이터가 일관성이 있는지 확인합니다. 언제든지 하나의 스레드 만 잠금을 획득하여 동기화 된 블록 또는 메소드 ( 중요 섹션 )에 액세스 할 수 있습니다. 다른 스레드는 임계 섹션 에 액세스하기 위해 잠금 해제를 기다립니다.

방법은 언제 동기화됩니까?

메서드 정의 또는 선언에 synchronized 를 추가하면 메서드가 동기화됩니다. 특정 코드 블록을 메소드에서 동기화 할 수도 있습니다.

문법적으로나 논리적으로 프로라는 것이 무엇을 의미합니까?

이는 하나의 스레드 만 잠금을 획득하여 중요 섹션 에 액세스 할 수 있음을 의미합니다. 이 스레드가이 잠금을 해제하지 않으면 다른 모든 스레드가 잠금을 획득하기 위해 대기해야합니다. 그들은 자물쇠를 구입하지 않고도 중요한 섹션 에 들어갈 수있는 권한이 없습니다.

이것은 마법으로 할 수 없습니다. 응용 프로그램에서 중요한 섹션 을 식별하고 적절히 보호하는 것은 프로그래머의 책임입니다. Java는 응용 프로그램을 보호하기위한 프레임 워크를 제공하지만 보호해야 할 모든 섹션이 프로그래머의 책임입니다.

java 문서 page 에서 자세한 내용보기

본질적인 잠금 및 동기화 :

동기화는 내장 잠금 또는 모니터 잠금이라고하는 내부 엔티티를 기반으로합니다. Intrinsic 잠금은 객체 상태에 대한 독점적 액세스를 수행하고 가시성에 필수적인 선행 관계를 설정하는 등의 두 가지 측면에서 동기화의 역할을합니다.

모든 객체에는 고유 한 잠금이 있습니다 . 관습 적으로, 객체의 필드에 대해 배타적이고 일관된 액세스가 필요한 스레드는 객체의 고유 한 잠금을 액세스하기 전에 가져와야하며, 잠금이 끝나면 내장 잠금을 해제해야합니다.

스레드는 잠금을 획득하고 잠금을 해제 한 시간 사이에 고유 잠금을 소유한다고합니다. 스레드가 고유 잠금을 소유하고있는 한 다른 스레드는 동일한 잠금을 획득 할 수 없습니다. 다른 스레드는 잠금 획득을 시도 할 때 차단됩니다.

스레드가 고유 잠금을 해제하면 해당 동작과 이후에 동일한 잠금을 획득 할 때 발생 - 관계가 설정됩니다.

동기화 된 메소드를 만드는 것은 두 가지 effects .

우선, 같은 오브젝트상의 동기 메소드의 2 회의 호출은 인터리브 할 수 없습니다.

한 스레드가 객체에 대해 동기화 된 메소드를 실행할 때 첫 번째 스레드가 객체로 완료 될 때까지 동일한 객체 블록 (실행 중단)에 대해 동기화 된 메소드를 호출하는 다른 모든 스레드.

둘째, 동기화 된 메소드가 종료되면 동일한 객체에 대한 동기화 된 메소드의 후속 호출과 자동으로 '발생 - 이전'관계를 설정합니다.

이렇게하면 모든 스레드에서 객체 상태에 대한 변경 사항을 볼 수 있습니다.

동기화의 다른 대안을 찾으십시오.

Java에서 동기화되지 않습니까?

synchronized 키워드의 사용법과 중요성에 대해 몇 가지 질문이 있습니다.

  • synchronized 키워드의 의미는 무엇입니까?
  • 메소드는 언제 synchronized 되어야합니까?
  • 프로그래밍 적으로나 논리적으로 무엇을 의미합니까?

개요

Java의 동기화 된 키워드는 스레드 안전성과 관련이 있습니다. 즉, 여러 스레드가 동일한 변수를 읽거나 쓰는 경우입니다.
이는 직접 (동일한 변수에 액세스하여) 또는 간접적으로 (동일한 변수에 액세스하는 다른 클래스를 사용하는 클래스를 사용하여) 발생할 수 있습니다.

synchronized 키워드는 여러 스레드가 동일한 변수에 안전하게 액세스 할 수있는 코드 블록을 정의하는 데 사용됩니다.

더 깊게

Syntax-wise는 synchronized 키워드가 Object 를 매개 변수 ( 잠금 객체 라고 함)로 취한 다음 { block of code } .

  • 실행시이 키워드가 발생하면 현재 스레드가 잠금 객체 를 "lock / acquire / own"(선택)하고 잠금을 획득 한 후 연관된 코드 블록을 실행하려고합니다.

  • 동기화 된 코드 블록 내부의 변수에 대한 모든 쓰기는 동일한 잠금 객체를 사용하여 동기화 된 코드 블록 내에서 코드를 유사하게 실행하는 다른 모든 스레드에서 볼 수 있습니다.

  • 한 번에 하나의 스레드 만 잠금을 유지할 수 있으며, 그 동안 동일한 잠금 객체 를 얻으려는 다른 모든 스레드는 대기합니다 (실행을 일시 중지합니다). 실행이 동기화 된 코드 블록을 종료하면 잠금이 해제됩니다.

동기화 된 메소드 :

메소드 정의에 synchronized 키워드를 추가하는 것은 잠금 객체this (예 : 메소드) 이고 ClassInQuestion.getClass() (클래스 메소드) 인 동기화 된 코드 블록에 래핑되는 전체 메소드 본문과 ClassInQuestion.getClass() .

- 인스턴스 메소드는 static 키워드가없는 메소드입니다.
- 클래스 메소드는 static 키워드를 가지는 메소드입니다.

전문인

동기화가 없으면 읽기 및 쓰기 순서가 보장되지 않으므로 변수에 가비지가 남을 수 있습니다.
(예를 들어 변수는 하나의 쓰레드에 의해 쓰여진 비트의 절반과 다른 쓰레드에 의해 쓰여진 비트의 절반으로 끝날 수 있습니다.

하드웨어가 변수의 값을 캐싱 할 수 있고 쓰레드가 쓰여진 값 대신 캐싱 된 값을 볼 수 있기 때문에 다른 쓰레드가 그것을 읽는 (wall-clock time) 전에 쓰기 작업을 완료하는 것만으로는 충분하지 않습니다 그것.

결론

따라서 Java의 경우 스레딩 오류가 발생하지 않도록 Java 메모리 모델을 따라야합니다.
다른 말로하면 : 동기화, 원자 적 연산 또는 클래스를 사용합니다.

출처

http://docs.oracle.com/javase/specs/jls/se8/html/index.html
Java® 언어 사양, 2015-02-13


synchronized는 멀티 스레딩 환경에서 관계가 발생하기 전에 메모리 불일치 및 스레드 간섭 오류를 피하기 위해 사용되는 Java의 키워드입니다.


나는 당신이 이미 당신의 답을 알고 있음을 압니다.
나는이 글을 쓰면서, 같은 질문을 가진 사람들을 돕기 위해이 페이지를 훑어보고있다.
여기에 effects 대한 설명이있다.

다음 코드를 살펴보십시오.

public class SynchronizedCounter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }

    public synchronized void decrement() {
        c--;
    }

    public synchronized int value() {
        return c;
    }
}

countSynchronizedCounter 의 인스턴스 인 경우 이러한 메서드를 동기화하면 두 가지 효과가 있습니다.

  • 우선, 같은 오브젝트상의 동기 메소드의 2 회의 호출은 인터리브 할 수 없습니다. 한 스레드가 객체에 대해 동기화 된 메소드를 실행할 때 첫 번째 스레드가 객체로 완료 될 때까지 동일한 객체 블록 (실행 중단)에 대해 동기화 된 메소드를 호출하는 다른 모든 스레드.
  • 둘째, 동기화 된 메소드가 종료되면 동일한 객체에 대한 동기화 된 메소드의 후속 호출과 자동으로 '발생 - 이전'관계를 설정합니다. 이렇게하면 모든 스레드에서 객체 상태에 대한 변경 사항을 볼 수 있습니다.

다른 답변이 누락 된 부분 중 하나는 중요한 장벽 인 메모리 장벽 입니다. 스레드 동기화는 기본적으로 직렬화와 가시성의 부분으로 구성됩니다. 나는 "jvm memory barrier"에 대해 모든 사람에게 google에 조언한다. 이는 여러 스레드가 공유하는 데이터를 수정하는 경우 매우 중요하지 않은 중요한 주제이기 때문이다. 이 작업을 수행 한 후에 명시 적 동기화 사용을 피하는 데 도움이되는 java.util.concurrent 패키지의 클래스를 살펴보고 프로그램을 간단하고 효율적으로 유지하는 데 도움이되며 교착 상태를 방지 할 수도 있습니다.

그러한 예가 ConcurrentLinkedDeque 입니다. 명령 패턴 과 함께 명령을 동시 대기열에 채워서 매우 효율적인 작업자 스레드를 작성할 수 있습니다. 명시 적 동기화가 필요없고 교착 상태가 발생하지 않으며 명시 적 sleep ()이 필요하지 않으며 take ()를 호출하여 대기열을 폴링하십시오.

간단히 말해, "메모리 동기화"는 암시 적으로 발생합니다. 스레드를 시작하면 스레드가 종료되고 휘발성 변수를 읽은 다음 모니터의 잠금을 해제합니다 (동기화 된 블록 / 함수는 그대로 두십시오). 이러한 "동기화"는 영향을줍니다 ( "플러시 ") 모든 특정 작업 전에 완료된 쓰기. 앞서 언급 한 ConcurrentLinkedDeque 의 경우 문서 "says":

메모리 일관성 효과 : 다른 동시 수집과 마찬가지로 ConcurrentLinkedDeque에 객체를 배치하기 전에 스레드의 작업 happen-before 합니다. 다른 스레드의 ConcurrentLinkedDeque에서 해당 요소에 액세스하거나 해당 요소를 제거한 후에 수행됩니다.

이 암시적인 동작은 다소 위험한 측면입니다. 많은 경험이없는 대부분의 자바 프로그래머는 주어진 이유로 많이 사용하게 될 것이기 때문입니다. 그리고 나서 Java가 다른 작업 부하가있는 "프로덕션"에서 수행해야 할 작업을 수행하지 않은 후에 갑자기이 스레드 위로 넘어져서 동시성 문제를 테스트하기가 매우 어렵습니다.


동기화는 단순히 단일 객체와 연결된 경우 여러 스레드가 특정 객체에서 동기화 된 블록을 사용하는 경우 더티 읽기 및 쓰기를 방지 할 수 있음을 의미합니다. 좀 더 명확하게하기 위해 예를 들어 보겠습니다.

class MyRunnable implements Runnable {
    int var = 10;
    @Override
    public void run() {
        call();
    }

    public void call() {
        synchronized (this) {
            for (int i = 0; i < 4; i++) {
                var++;
                System.out.println("Current Thread " + Thread.currentThread().getName() + " var value "+var);
            }
        }
    }
}

public class MutlipleThreadsRunnable {
    public static void main(String[] args) {
        MyRunnable runnable1 = new MyRunnable();
        MyRunnable runnable2 = new MyRunnable();
        Thread t1 = new Thread(runnable1);
        t1.setName("Thread -1");
        Thread t2 = new Thread(runnable2);
        t2.setName("Thread -2");
        Thread t3 = new Thread(runnable1);
        t3.setName("Thread -3");
        t1.start();
        t2.start();
        t3.start();
    }
}

두 개의 MyRunnable 클래스 객체를 만들었습니다. runnable1은 스레드 1과 공유되고 스레드 3과 runnable2는 스레드 2와 공유됩니다. 이제 동기화가 사용되지 않고 t1과 t3이 시작될 때 스레드 1과 3이 동시에 var 값에 영향을 미친다는 PFB 출력이 있습니다. 여기서 스레드 2의 경우 var에 자체 메모리가 있습니다.

Without Synchronized keyword

    Current Thread Thread -1 var value 11
    Current Thread Thread -2 var value 11
    Current Thread Thread -2 var value 12
    Current Thread Thread -2 var value 13
    Current Thread Thread -2 var value 14
    Current Thread Thread -1 var value 12
    Current Thread Thread -3 var value 13
    Current Thread Thread -3 var value 15
    Current Thread Thread -1 var value 14
    Current Thread Thread -1 var value 17
    Current Thread Thread -3 var value 16
    Current Thread Thread -3 var value 18

Synchronzied를 사용하면 스레드 3이 모든 시나리오에서 완료 될 때까지 대기합니다. 두 개의 잠금이 획득됩니다. 하나는 스레드 1과 스레드 3이 공유하는 runnable1이고 다른 하나는 스레드 2 만 공유하는 runnable2입니다.

Current Thread Thread -1 var value 11
Current Thread Thread -2 var value 11
Current Thread Thread -1 var value 12
Current Thread Thread -2 var value 12
Current Thread Thread -1 var value 13
Current Thread Thread -2 var value 13
Current Thread Thread -1 var value 14
Current Thread Thread -2 var value 14
Current Thread Thread -3 var value 15
Current Thread Thread -3 var value 16
Current Thread Thread -3 var value 17
Current Thread Thread -3 var value 18

축구 그라운드에서 찾을 수있는 일종의 개찰구라고 생각하십시오. 들어가기를 원하는 사람들의 평행 증기가 있지만 개찰구에서 '동기화'됩니다. 한 번에 한 사람 만 통과 할 수 있습니다. 통과하려는 모든 사람들이 할 것이지만, 그들이 통과 할 때까지 기다려야 할 수도 있습니다.


Synchronized normal method Synchronized statement 동등한 Synchronized normal method (사용)

class A {
    public synchronized void methodA() {
        // all function code
    }

    equivalent to

    public void methodA() {
        synchronized(this) {
             // all function code
        }
    } 
}

Synchronized static method Synchronized statement 동등한 Synchronized static method (클래스 사용)

class A {
    public static synchronized void methodA() {
        // all function code
    }

    equivalent to

    public void methodA() {
        synchronized(A.class) {
             // all function code
        }
    } 
}

동기화 된 문 (변수 사용)

class A {
    private Object lock1 = new Object();

    public void methodA() {
        synchronized(lock1 ) {
             // all function code
        }
    } 
}

synchronized 경우 Synchronized MethodsSynchronized Statements 이 모두 있습니다. 그러나 Synchronized MethodsSynchronized Statements 와 유사하므로 Synchronized Statements 를 이해하면됩니다.

=> 기본적으로, 우리는

synchronized(object or class) { // object/class use to provides the intrinsic lock
   // code 
}

여기에 2는 synchronized 이해를 돕는 것이라고 생각합니다.

  • 모든 객체 / 클래스에는 intrinsic lock 이 있습니다.
  • 스레드가 synchronized statement 호출하면 자동으로 해당 synchronized statement's 개체에 대한 intrinsic lock 을 가져 와서 메서드가 반환 될 때 해제합니다. 스레드가 intrinsic lock 소유하고있는 한, 다른 스레드는 SAME 잠금 => 스레드 안전을 획득 할 수 없습니다 .

=> thread Asynchronized(this){// code 1} => 모든 블록 코드 (내부 클래스)를 호출하면 SAME 잠금으로 인해 모든 synchronized normal method (내부 클래스)가 잠겨집니다. thread A 잠금 해제 ( "// 코드 1"완료) 후에 실행됩니다.

이 동작은 synchronized(a variable){// code 1} 또는 synchronized(class) 와 유사합니다.

SAME LOCK => lock (어떤 메소드 또는 어떤 문장에 의존하지 않는가?)

동기화 된 메서드 또는 동기화 된 문 사용?

나는 그것이 더 확장 가능하기 때문에 synchronized statements 선호한다. 예를 들어, 나중에 메소드의 일부만 동기화하면됩니다. 예를 들어 동기화 된 메서드가 2 개 있고 서로 관련성이 없지만 스레드가 메서드를 실행할 때 다른 메서드를 차단합니다 ( synchronized(a variable) 사용)을 방지 할 수 synchronized(a variable) ).

그러나 동기화 된 메소드를 적용하는 것은 간단하고 코드는 단순 해 보입니다. 어떤 클래스의 경우, 오직 하나의 동기화 된 메소드, 또는 서로 관련된 클래스의 모든 동기화 된 메소드 => synchronized method 를 사용하여 코드를 더 짧고 이해하기 쉽게 만들 수 있습니다

노트

( synchronized 와 관련이 없으며 객체와 클래스 또는 정적이 아니고 정적 인 경우와 다릅니다.

  • synchronized 또는 normal 메소드 또는 synchronized(this) 또는 synchronized(non-static variable) 하면 각 객체 인스턴스의 기반을 동기화합니다.
  • synchronized 또는 정적 메서드 또는 synchronized(class) 또는 synchronized(static variable) 하면 클래스 기반을 동기화합니다.

참고

effects page

희망이 도움이


synchronized 키워드는 메소드를 입력 할 때 스레드가 잠금을 가져 오도록하여 하나의 스레드 만 (정적 메소드가 아닌 한 주어진 객체 인스턴스에 대해) 메소드를 동시에 실행할 수 있도록합니다.

이것은 종종 클래스를 thread-safe로 만드는 것으로 알려져 있지만, 이것이 완곡 어법이라고 말할 수 있습니다. 동기화가 벡터의 내부 상태가 손상되지 않도록 동기화하는 것은 사실이지만 일반적으로 Vector를 많이 사용하지는 않습니다.

이걸 고려하세요:

 if (vector.isEmpty()){
     vector.add(data);
 }

관련 메소드가 동기화되어 있어도 개별적으로 잠기고 잠금 해제되어 있기 때문에 불행히도 시간이 초과 된 두 개의 스레드는 두 개의 요소가있는 벡터를 만들 수 있습니다.

따라서 실제로 애플리케이션 코드에서 동기화해야합니다.

메소드 레벨 동기화는 a) 필요치 않을 때 비싸기 때문에 b) 동기화가 불충분 할 때 불충분합니다. 이제는 동기화되지 않은 대체가 있습니다 (Vector의 경우 ArrayList).

최근에는 다중 스레드 문제를 처리하는 영리한 유틸리티가 포함 된 동시성 패키지가 릴리스되었습니다.


synchronized 키워드는 모두 동일한 변수, 객체 및 자원을 읽고 쓰는 다른 스레드에 관한 것입니다. 이것은 Java에서 사소한 주제는 아니지만 Sun의 인용입니다.

synchronized 메서드는 스레드 간섭 및 메모리 일관성 오류를 방지하기위한 간단한 전략을 가능하게합니다. 객체가 둘 이상의 스레드에서 볼 수있는 경우 해당 객체의 변수에 대한 모든 읽기 또는 쓰기는 동기화 된 메서드를 통해 수행됩니다.

아주 작은 요약 : 동일한 '리소스'에 대해 읽고 쓰는 두 개의 스레드가있을 때 foo 라는 변수를 말하면 이러한 스레드가 원자 적 방법으로 변수에 액세스하는지 확인해야합니다. synchronized 키워드가 없으면 스레드 1은 foo 로 변경된 스레드 2를 보지 못하거나 더 나쁜 경우에만 절반이 변경 될 수 있습니다. 이것은 논리적으로 기대하는 것이 아닙니다.

다시 말하지만, 이것은 Java에서 사소한 주제입니다. 자세한 내용을 보려면 SO 및 Interwebs에 대한 다음 항목을 살펴보십시오.

"Brian Goetz" 라는 이름이 당신의 두뇌에서 "동시성" 이라는 용어와 영구적으로 연관 될 때까지이 주제들을 계속 탐구하십시오.





synchronized