sourcecode

volatile 키워드는 무엇에 도움이 됩니까?

copyscript 2022. 11. 26. 08:45
반응형

volatile 키워드는 무엇에 도움이 됩니까?

늘늘 the the the the the the the를volatile키워드를 지정합니다.잘 모르기 때문에 나는 이 설명을 발견했다.

그 기사에서 문제의 키워드를 상세하게 설명하고 있는 것에 비추어 볼 때, 당신은 그것을 사용한 적이 있습니까, 아니면 이 키워드를 올바르게 사용할 수 있는 경우를 본 적이 있습니까?

volatile에는 메모리 가시성에 대한 의미론이 있습니다.basically적 of 의 값입니다.volatile필드는 쓰기 작업이 완료된 후 모든 독서자(특히 다른 스레드)에 표시됩니다.volatile몇을 볼 수 있었습니다 , , , , 는 것을 알 수 있었습니다.

네,, 네, 네, 네, 사를 씁니다.volatile루프를 입니다.에 의해 됩니다.volatile합니다.true. 은 음음음음 음 be . . . to to to .로 설정할 수false'멈추다' 인식됩니다.false된 후 됩니다.

내가 강력히 추천하는 책 "Java Concurrency in Practice"는 좋은 설명을 해준다.volatile이 책은 질문에서 언급된 IBM 기사를 쓴 사람이 쓴 것입니다(사실 그는 그 기사의 맨 아래에 자신의 책을 인용하고 있습니다).용용 of의 volatile그의 글에서 "패턴 1 상태 플래그"라고 부르는 것입니다.

후드에서 작동하는 방법에 대해 자세히 알고 싶다면 Java 메모리 모델을 읽어보십시오.이 수준을 넘어서고 싶다면 Hennessy & Patterson과 같은 좋은 컴퓨터 아키텍처 책을 참조하여 캐시의 일관성과 캐시의 일관성에 대해 읽어보십시오.

"..." "이렇게 하면 안 돼요" - - - - - - -

volatile, 원자 거동을 다루는 패키지에 대해 자세히 읽어보십시오.

싱글톤 패턴의 위키피디아 게시물은 사용 중인 휘발성을 보여줍니다.

휘발성(vllttʌl):상온에서 쉽게 증발함

관한 한 점volatile:

  1. Java 를 하면 Java 의 합니다.synchronized ★★★★★★★★★★★★★★★★★」volatile잠금 장치도 있습니다.
  2. 를 사용할 수 .synchronized. ㅇㅇㅇ를 사용해서.synchronized변수가 있는 키워드는 부정하며 컴파일 오류가 발생합니다.「 」를하는 대신에, 「 」를 사용합니다.synchronized변수에서는 , Java 를 사용할 수 있습니다.volatile JVM 에 값을 읽도록 합니다.volatile로컬로 캐시하지 않고 기본 메모리에서 변수를 선택합니다.
  3. 되지 않는 " "를 .volatile키워드를 지정합니다.

원천

「 」의 사용 예volatile:

public class Singleton {
    private static volatile Singleton _instance; // volatile variable
    public static Singleton getInstance() {
        if (_instance == null) {
            synchronized (Singleton.class) {
                if (_instance == null)
                    _instance = new Singleton();
            }
        }
        return _instance;
    }
}

첫 번째 요청이 왔을 때 인스턴스를 느릿느릿 만들고 있습니다.

「 」를 , 「 」가 됩니다._instance "dariable''volatile '나다'의 입니다.Singleton하다 스레드 CPU가 파손되는 다른 는 "A"의 을 볼 수._instanceNULL이 아닌 것으로 간주되며, 여전히 NULL로 할당되어 있다고 간주됩니다.

왜런이 이이 ?? ???, 값은 되지 ._instance는 메인 메모리에서는 갱신되지 않습니다.Java에서 Volatile 키워드를 사용하면 Java 자체에서 처리되며 이러한 업데이트는 모든 리더 스레드에서 볼 수 있습니다.

결론:volatile키워드는 스레드 간의 메모리 내용 통신에도 사용됩니다.

휘발성이 없는의 사용 예:

public class Singleton{    
    private static Singleton _instance;   //without volatile variable
    public static Singleton getInstance(){   
          if(_instance == null){  
              synchronized(Singleton.class){  
               if(_instance == null) _instance = new Singleton(); 
      } 
     }   
    return _instance;  
    }

이치노JIT 컴파일러는 동기화된 블록 내에서 인스턴스 값을 다시 한 번 확인하지만(성능상의 이유로), 생성자가 실행을 완료하기 전에 인스턴스에 대한 참조가 설정되도록 바이트 코드를 재정렬할 수 있습니다., 는 완전히수 개체를 합니다.코드 스레드를 안전하게 하기 위해 인스턴스 변수에 대해 Java 5 이후 volatile 키워드를 사용할 수 있습니다.휘발성으로 표시된 변수는 개체 생성자가 실행을 완료한 후에만 다른 스레드에 표시됩니다.
★★★

여기에 이미지 설명 입력

volatile Java에서의 사용:

Fail-Fast Referator는 일반적으로 다음 명령어를 사용하여 구현됩니다.volatile카운터가 표시됩니다.

  • 리스트가 갱신되면 카운터가 증가합니다.
  • 「 」의 Iterator이 "에됩니다.Iterator★★★★★★ 。
  • 「 」의 Iterator되어 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」의 「2」가 슬로우 됩니다.ConcurrentModificationException만약 다르다면요.

Fail Safe Iterator의 실장은 일반적으로 가볍습니다.일반적으로 특정 목록 구현의 데이터 구조 속성에 의존합니다.일반적인 패턴은 없습니다.

volatile는 스레드를 정지하는 데 매우 유용합니다.

자체 스레드를 작성해야 하는 것이 아니라 Java 1.6에는 멋진 스레드 풀이 많이 있습니다.하지만 실타래가 꼭 필요하다면, 어떻게 막을지 알아야 합니다.

스레드에 사용하는 패턴은 다음과 같습니다.

public class Foo extends Thread {

  private volatile boolean close = false;

  public void run() {
    while(!close) {
      // do work
    }
  }
  public void close() {
    close = true;
    // interrupt here if needed
  }
}

는 " " "로 표시됩니다.close는 while을 .close()휘발성이 없으면 루프를 실행하고 있는 스레드는 닫는 변경을 인식하지 못할 수 있습니다.

동기화가 불필요하다는 것을 알 수 있습니다.

으로 volatile키워드에는 특별한 두 가지 주요 특성이 있습니다.

  1. 휘발성 변수가 있는 경우 스레드에 의해 컴퓨터(마이크로프로세서) 캐시 메모리에 캐시될 수 없습니다.액세스는 항상 메인 메모리에서 이루어집니다.

  2. 휘발성 변수에 기입 조작이 진행 중이고, 갑자기 읽기 조작이 요구되었을 경우, 읽기 조작 전에 기입 조작이 완료되는 것이 보증된다.

위의 두 가지 특성이 그것을 추론한다.

  • 휘발성 변수를 읽는 모든 스레드는 확실히 최신 값을 읽습니다.어떤 캐시 값도 그것을 오염시킬 수 없기 때문입니다.또한 읽기 요청은 현재 쓰기 작업이 완료된 후에만 허용됩니다.

그리고 다른 한편으로

  • 제가 말씀드린 #2를 더 조사하면 알 수 있습니다.volatile키워드는 'n'개수의 리더 스레드와 1개의 라이터 스레드만 있는 공유 변수를 유지하는 이상적인 방법입니다.를 추가하면volatile워드,, 완완나사산 안전에 대한 다른 오버헤드는 없습니다.

친근하게

사용할 없습니다.volatile키워드만, 복수의 라이터 스레드가 액세스 하고 있는 공유 변수를 만족시킵니다.

「 」를 사용하는 인 예.volatile은요, 사용법입니다.volatile boolean합니다.스레드를 시작하고 다른 스레드에서 스레드를 안전하게 인터럽트하려면 스레드에서 플래그를 주기적으로 검사하도록 할 수 있습니다.사실를 ' by 、 [ by ( 을 ) 。volatile는, 했을 때에 것을 할 수 이때, 「네, 네, 네, 네, 네, 네, 네, 네, 네, 네, 네, 네, 네, 네, 네, 네, 네, 네, 네, 네, 네, 네, 네, 네, 세팅되어 있습니다.synchronized

예, 가변 변수를 여러 스레드로 액세스하려면 항상 휘발성을 사용해야 합니다.일반적으로 단일 원자 조작(예를 들어 변수 상태를 수정하기 전에 확인) 이상을 수행해야 하며, 이 경우 동기화된 블록을 대신 사용해야 하므로 그다지 일반적인 사용 사례는 아닙니다.

장변형 및 이중변형 유형에 대한 읽기 및 쓰기 작업의 처리에 대해서는 아무도 언급하지 않았습니다.읽기 및 쓰기는 참조 변수 및 대부분의 원시 변수(긴 변수 및 이중 변수 유형 제외)에 대한 원자 연산이며, 이러한 유형의 경우 volatile 키워드를 사용하여 원자 연산을 수행해야 합니다.@link

휘발성

volatile->synchronized [대략]

volatile프로그래머의 경우 값은 항상 최신이 됩니다.문제는 값을 다른 유형의 하드웨어 메모리에 저장할 수 있다는 것입니다. 캐시, 및 할 수 .

여기에 이미지 설명 입력

volatile키워드는 변수를 RAM 메모리에서 직접 읽고 쓰는 것을 나타냅니다.컴퓨팅 풋프린트가 있습니다.

Java 5 '''volatile by by】를 지원함으로써happens-before [대략]

이후 필드를 읽기 전에 휘발성 필드에 쓰기가 발생합니다.

Read is after write

volatile키워드로는 해결되지 않는다.race condition여러 스레드가 동시에 몇 가지 을 쓸 수 있는 상황입니다.그 대답은.synchronized키워드[대략]

그 결과 한 스레드는 쓰고 다른 스레드는 읽기만 하면 안전합니다.volatile

volatile 키워드를 사용하는 스레드 정지 이외의 2가지 중요한 시나리오가 있다고 생각합니다.

  1. 잠금 메커니즘을 이중으로 확인.싱글톤 디자인 패턴에 자주 사용됩니다.이 경우 싱글톤 객체는 휘발성으로 선언해야 합니다.
  2. 스플리어스 웨이크업통지 콜이 발행되지 않은 경우에도 스레드가 대기 콜에서 웨이크업될 수 있습니다.이 동작을 스플리어스 웨이크업이라고 합니다.이것은 조건 변수(부울 플래그)를 사용하여 대응할 수 있습니다.플래그가 true인 한 wait() 콜을 while 루프에 넣습니다.따라서 Notify/Notify 이외의 이유로 인해 스레드가 콜 대기 상태에서 해제된 경우그 이외의 플래그는 모두 true이므로 콜은 다시 대기합니다.알림을 호출하기 전에 이 플래그를 true로 설정합니다.이 경우 부울 플래그는 휘발성으로 선언됩니다.

변수합니다( 경우).volatile해당 변수의 수식자.다른 스레드는 RAM 메모리 대신 CPU 캐시에서 변수 값을 읽기 때문에 이 변수의 값을 읽지 않습니다. '아까', '아까', '아까', '아까'Visibility Problem.

변수 「」를 으로써,volatile카운터 변수에 대한 모든 쓰기는 즉시 메인 메모리에 다시 기록됩니다.또한 카운터 변수의 모든 읽기는 메인 메모리에서 직접 읽힙니다.

public class SharedObject {
    public volatile int sharedVariable = 0;
}

비휘발성 변수에서는 Java Virtual Machine(JVM; Java 가상 머신)이 메인 메모리에서 CPU 캐시로 데이터를 읽거나 CPU 캐시에서 메인 메모리에 데이터를 쓰는 타이밍에 대한 보장은 없습니다.이로 인해 몇 가지 문제가 발생할 수 있습니다.이 문제는 다음 섹션에서 설명하겠습니다.


예:

두 개 이상의 스레드가 다음과 같이 선언된 카운터 변수를 포함하는 공유 개체에 액세스할 수 있는 상황을 상상해 보십시오.

public class SharedObject {
    public int counter = 0;
}

스레드 1만이 카운터 변수를 증가시키지만 스레드1과 스레드2는 모두 카운터 변수를 읽을 수 있습니다.

카운터 변수가 volatile로 선언되지 않은 경우 카운터 변수의 값이 CPU 캐시에서 메인메모리로 언제 다시 입력되는지 보증할 수 없습니다.즉, CPU 캐시의 카운터 변수 값이 메인 메모리와 다를 수 있습니다.이 상황은 다음과 같습니다.

휘발성의

변수의 최신 값이 다른 스레드에 의해 메인 메모리에 아직 기록되지 않았기 때문에 스레드에 표시되지 않는 문제를 "가시성" 문제라고 합니다.한 스레드의 업데이트는 다른 스레드에 표시되지 않습니다.

멀티스레드 응용 프로그램을 개발하려면 'volatile' 키워드 또는 'synchronized' 및 기타 동시성 제어 도구와 기술을 사용해야 합니다.이러한 애플리케이션의 예로는 데스크톱 앱이 있습니다.

애플리케이션 서버에 도입하는 애플리케이션(Tomcat, JBoss AS, Glassfish 등)을 개발하고 있는 경우, 애플리케이션 서버에 의해서 이미 처리되고 있는 동시성 제어를 스스로 처리할 필요는 없습니다.실제로 Java EE 표준은 '인프라' 계층의 일부이므로 서블릿 및 EJB에서 동시 제어가 금지되어 있으므로 이를 처리할 필요가 없습니다.이러한 앱에서는 싱글톤 개체를 구현하는 경우에만 동시성 제어를 수행할 수 있습니다.스프링과 같은 프레임워크를 사용하여 컴포넌트를 짜는 경우에도 이 문제는 이미 해결되었습니다.

따라서 애플리케이션이 웹 애플리케이션이고 Spring이나 EJB와 같은 IOC 프레임워크를 사용하는 Java 개발의 대부분의 경우 '휘발성'을 사용할 필요가 없습니다.

volatile는 모든 스레드(그 자체도)가 증가하고 있음을 보증할 뿐입니다.예를 들어, 카운터는 변수의 같은 면을 동시에 확인합니다.동기화, 원자성 또는 다른 것 대신 사용되지 않고 읽기를 완전히 동기화합니다.자바아래 예에서 보듯이 휘발성 변수 연산도 원자성이기 때문에 한 번에 실패하거나 성공합니다.

package io.netty.example.telnet;

import java.util.ArrayList;
import java.util.List;

public class Main {

    public static volatile  int a = 0;
    public static void main(String args[]) throws InterruptedException{

        List<Thread> list = new  ArrayList<Thread>();
        for(int i = 0 ; i<11 ;i++){
            list.add(new Pojo());
        }

        for (Thread thread : list) {
            thread.start();
        }

        Thread.sleep(20000);
        System.out.println(a);
    }
}
class Pojo extends Thread{
    int a = 10001;
    public void run() {
        while(a-->0){
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Main.a++;
            System.out.println("a = "+Main.a);
        }
    }
}

휘발성과 비휘발성 결과는 항상 다릅니다.그러나 아래와 같이 AtomicInteger를 사용하면 결과는 항상 동일합니다.이는 동기화된 경우에도 마찬가지입니다.

    package io.netty.example.telnet;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;

    public class Main {

        public static volatile  AtomicInteger a = new AtomicInteger(0);
        public static void main(String args[]) throws InterruptedException{

            List<Thread> list = new  ArrayList<Thread>();
            for(int i = 0 ; i<11 ;i++){
                list.add(new Pojo());
            }

            for (Thread thread : list) {
                thread.start();
            }

            Thread.sleep(20000);
            System.out.println(a.get());

        }
    }
    class Pojo extends Thread{
        int a = 10001;
        public void run() {
            while(a-->0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Main.a.incrementAndGet();
                System.out.println("a = "+Main.a);
            }
        }
    }

여기에 기재되어 있는 답변에는 좋은 이론적 설명이 많이 있습니다만, 여기에 설명과 함께 실용적인 예를 추가하겠습니다.

1.

휘발성 사용을 수반하지 않는 코드 실행

public class VisibilityDemonstration {

private static int sCount = 0;

public static void main(String[] args) {
    new Consumer().start();
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        return;
    }
    new Producer().start();
}

static class Consumer extends Thread {
    @Override
    public void run() {
        int localValue = -1;
        while (true) {
            if (localValue != sCount) {
                System.out.println("Consumer: detected count change " + sCount);
                localValue = sCount;
            }
            if (sCount >= 5) {
                break;
            }
        }
        System.out.println("Consumer: terminating");
    }
}

static class Producer extends Thread {
    @Override
    public void run() {
        while (sCount < 5) {
            int localValue = sCount;
            localValue++;
            System.out.println("Producer: incrementing count to " + localValue);
            sCount = localValue;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                return;
            }
        }
        System.out.println("Producer: terminating");
    }
}
}

위 코드에는 생산자와 소비자라는 두 가지 스레드가 있습니다.

생산자 스레드는 그 사이에 루프를 5회 반복(1000밀리초 또는 1초의 sleep)합니다.모든 반복에서 생산자 스레드는 sCount 변수 값을 1씩 증가시킵니다.따라서 생산자는 모든 반복에서 sCount 값을 0에서 5로 변경합니다.

컨슈머 스레드는 일정한 루프에 있으며 sCount 값이 5에 도달할 때까지 항상 인쇄됩니다.

두 루프가 동시에 시작됩니다.따라서 생산자와 소비자 모두 sCount 값을 5회 인쇄해야 합니다.

산출량

Consumer: detected count change 0
Producer: incrementing count to 1
Producer: incrementing count to 2
Producer: incrementing count to 3
Producer: incrementing count to 4
Producer: incrementing count to 5
Producer: terminating

분석.

상기 프로그램에서는 생산자 스레드가 sCount 값을 갱신하면 메인 메모리(모든 스레드가 처음에 변수 값을 읽는 메모리)에서 변수 값을 갱신합니다.단, 컨슈머 스레드는 이 메인메모리에서 sCount 값을 처음 읽은 후 해당 변수 값을 자신의 메모리 내에 캐시합니다.따라서 메인 메모리의 원래 sCount 값이 프로듀서 스레드에 의해 갱신되어도 컨슈머 스레드는 갱신되지 않은 캐시된 값에서 읽습니다.이를 가시성 문제라고 합니다.

2.

휘발성 사용을 수반하는 코드 실행

위의 코드에서 sCount가 선언된 코드 행을 다음과 같이 바꿉니다.

private volatile  static int sCount = 0;

산출량

Consumer: detected count change 0
Producer: incrementing count to 1
Consumer: detected count change 1
Producer: incrementing count to 2
Consumer: detected count change 2
Producer: incrementing count to 3
Consumer: detected count change 3
Producer: incrementing count to 4
Consumer: detected count change 4
Producer: incrementing count to 5
Consumer: detected count change 5
Consumer: terminating
Producer: terminating

분석.

변수 휘발성을 선언하면 이 변수 또는 이 변수로부터의 모든 읽기 및 쓰기가 메인메모리에 직접 들어가는 것을 의미합니다.이러한 변수의 값은 캐시되지 않습니다.

sCount 변수 값은 스레드에 의해 캐시되지 않으므로 사용자는 항상 메인 메모리(프로듀서 스레드에 의해 갱신되고 있음)에서 sCount의 원래 값을 읽습니다.따라서 이 경우 두 스레드가 서로 다른 sCount 값을 5회 인쇄하는 경우의 출력은 정확합니다.

이렇게 volatile 키워드를 사용하면 가시성 문제가 해결됩니다.

네, 많이 사용하고 있습니다.멀티 스레드 코드에 매우 편리합니다.당신이 지적한 글은 좋은 것입니다.명심해야 할 두 가지 중요한 사항이 있습니다.

  1. volatile의 기능과 동기화의 차이점을 완전히 이해하고 있는 경우에만 volatile을 사용해야 합니다.휘발성을 잘 이해하면 동기화가 유일한 옵션이라는 것이 명확해지는 경우가 많은 경우, 표면적으로는 휘발성이 동기화에 대한 보다 단순한 대안으로 보입니다.
  2. 휘발성은 실제로 많은 오래된 JVM에서는 동작하지 않지만 동기화된 JVM에서는 동작합니다.JVM별로 다양한 지원 수준을 참조한 문서를 본 기억이 있지만 지금은 찾을 수 없습니다.Java 1.5 이전 버전을 사용하는 경우 또는 프로그램이 실행될 JVM을 제어할 수 없는 경우 반드시 검토하십시오.

휘발성 필드에 액세스하는 모든 스레드는 캐시된 값을 사용하는 대신 계속하기 전에 현재 값을 읽습니다.

멤버 변수만 휘발성 또는 일시적일 수 있습니다.

물론입니다. (Java뿐만 아니라 C#에서도 마찬가지입니다.)예를 들어 int나 boolean 등 특정 플랫폼에서 원자적인 조작이 보증되지만 스레드 잠금의 오버헤드가 필요하지 않은 값을 가져오거나 설정해야 할 경우가 있습니다.volatile 키워드를 사용하면 값을 읽을 때 현재 값을 얻을 수 있습니다.다른 스레드에서의 쓰기로 인해 캐시된 값이 사용되지 않습니다.

volatile 키워드에는 두 가지 다른 사용법이 있습니다.

  1. JVM이 레지스터에서 값을 읽을 수 없도록 하고(캐시로 가정) 해당 값을 메모리에서 강제로 읽습니다.
  2. 메모리 불일치 오류의 위험을 줄입니다.

JVM이 레지스터의 값을 읽지 못하도록 하고 해당 값을 메모리에서 강제로 읽습니다.

비지 플래그는 장치가 비지 상태이고 플래그가 잠금으로 보호되지 않는 동안 스레드가 계속되지 않도록 하기 위해 사용됩니다.

while (busy) {
    /* do something else */
}

다른 스레드가 비지 플래그를 끄면 테스트 스레드가 계속됩니다.

busy = 0;

단, 비지는 테스트 스레드에서 자주 액세스되기 때문에 JVM은 비지 값을 레지스터에 배치하여 테스트를 최적화한 후 매번 테스트하기 전에 메모리의 비지 값을 읽지 않고 레지스터의 내용을 테스트할 수 있습니다.테스트 스레드에서는 비지 변경이 발생하지 않으며 다른 스레드는 메모리의 비지 값만 변경되므로 교착 상태가 발생합니다.비지 플래그를 volatile로 선언하면 각 테스트 전에 해당 값을 강제로 읽습니다.

메모리 일관성 오류의 위험을 줄입니다.

휘발성 변수를 사용하면 메모리 일관성 오류의 위험을 줄일 수 있습니다. 휘발성 변수에 대한 쓰기는 동일한 변수의 후속 읽기와의 "hapens-before" 관계를 설정하기 때문입니다.즉, 휘발성 변수에 대한 변경은 항상 다른 스레드에 표시됩니다.

메모리 일관성 오류 없이 읽고 쓰는 기술을 원자 작용이라고 합니다.

원자 작용은 효과적으로 동시에 일어나는 작용이다.원자 작용은 중간에 멈출 수 없다: 완전히 일어나거나 전혀 일어나지 않는다.원자 작용의 부작용은 작용이 완료될 때까지 보이지 않는다.

다음으로 atomic을 지정할 수 있는 액션을 나타냅니다.

  • 읽기 및 쓰기는 참조 변수 및 대부분의 원시 변수(긴 변수와 이중 변수를 제외한 모든 유형)에 대해 원자적입니다.
  • 읽기 및 쓰기는 휘발성으로 선언된 모든 변수(긴 변수 및 이중 변수 포함)에 대해 원자적입니다.

건배!

휘발성이 계속됩니다.

1> 다른 스레드별 휘발성 변수 읽기 및 쓰기는 스레드 자체 캐시 또는 CPU 레지스터가 아니라 항상 메모리에서 이루어집니다.따라서 각 스레드는 항상 최신 값을 처리합니다.2 > 2개의 다른 스레드가 동일한 인스턴스 또는 힙 내의 정적 변수와 함께 동작할 경우 다른 스레드의 액션이 잘못된 것으로 표시될 수 있습니다.제레미 맨슨의 블로그를 봐하지만 휘발성이 도움이 돼요

완전히 실행 중인 다음 코드는 다수의 스레드가 synchronized 키워드를 사용하지 않고 사전 정의된 순서로 실행되어 출력을 인쇄하는 방법을 보여 줍니다.

thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3

이를 위해 다음과 같은 완전한 실행 코드를 사용할 수 있습니다.

public class Solution {
    static volatile int counter = 0;
    static int print = 0;
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Thread[] ths = new Thread[4];
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new Thread(new MyRunnable(i, ths.length));
            ths[i].start();
        }
    }
    static class MyRunnable implements Runnable {
        final int thID;
        final int total;
        public MyRunnable(int id, int total) {
            thID = id;
            this.total = total;
        }
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (true) {
                if (thID == counter) {
                    System.out.println("thread " + thID + " prints " + print);
                    print++;
                    if (print == total)
                        print = 0;
                    counter++;
                    if (counter == total)
                        counter = 0;
                } else {
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException e) {
                        // log it
                    }
                }
            }
        }
    }
}

다음 github 링크에는 readme가 있어 적절한 설명을 제공합니다.https://github.com/sankar4git/volatile_thread_ordering

Oracle 문서 페이지에서 메모리 일관성 문제를 해결하기 위해 휘발성 변수가 필요합니다.

휘발성 변수를 사용하면 메모리 일관성 오류의 위험을 줄일 수 있습니다. 휘발성 변수에 대한 쓰기는 동일한 변수의 후속 읽기와 occurs-before 관계를 설정하기 때문입니다.

, 이 은 이 '아, 아, 아, 아, 아, 아, 로 바뀐다.volatile변수는 항상 다른 스레드에 표시됩니다.변수를 때 만 볼 수 있는 것이 .volatile변경을 일으킨 코드의 부작용도 있습니다.

에서 와 같이Peter Parker「」이 「」가 합니다.volatile수식자, 각 스레드의 스택은 변수의 고유한 복사본을 가질 수 있습니다.를 음음음음음음음음 로 한다.volatile메모리의 일관성 문제는 해결되었습니다.

자세한 내용은 젠코프 튜토리얼 페이지를 참조하십시오.

휘발성 및 휘발성 사용에 대한 자세한 내용은 관련 SE 질문을 참조하십시오.

Java에서 휘발성과 동기화의 차이

하나의 실용적인 사용 사례:

을 특정 스레드가 . 예를 현재 시각을 인쇄해야 합니다(예: : 、 음 、 음 、 음 、 음 、 음 、 음 、 음 、 음 、 you 음 you 。java.text.SimpleDateFormat("HH-mm-ss") 시간을 하는 클래스는 1로 할 수 것에의 、 재재의이이이SimpleDateFormat1번다른 모든 스레드는 이 휘발성 변수를 사용하여 현재 시간을 로그 파일에 인쇄할 수 있습니다.

휘발성 변수는 경량 동기화입니다.모든 스레드 간에 최신 데이터를 확인할 수 있어야 하며 원자성이 손상될 수 있는 경우에는 휘발성 변수를 사용해야 합니다.휘발성 변수에 대한 읽기에서는 레지스터나 다른 프로세서가 인식할 수 없는 캐시에 캐시되지 않기 때문에 스레드에 의해 수행된 최신 쓰기가 항상 반환됩니다.휘발성은 잠기지 않습니다.위에서 언급한 기준에 부합하는 시나리오일 경우 휘발성을 사용합니다.

volatile variable은 기본적으로 메인 공유 캐시 라인이 업데이트되면 즉시 업데이트(메인 공유 캐시 라인)에 사용됩니다.이것에 의해, 변경은 모든 워커 스레드에 곧바로 반영됩니다.

변수와 함께 사용하면 이 변수를 읽는 스레드에도 동일한 값이 표시됩니다.변수에 대한 읽기 및 쓰기가 여러 스레드가 있는 경우 변수를 휘발성으로 만드는 것만으로는 충분하지 않고 데이터가 손상됩니다.이미지 스레드는 같은 값을 읽었지만 각각 몇 가지 변경(카운터 증가 등)이 이루어졌습니다.메모리에 회신할 때 데이터 무결성이 침해됩니다.그렇기 때문에 variable을 동기화할 필요가 있습니다(다양한 방법이 가능합니다).

1개의 스레드에 의해 변경되고 다른 스레드가 이 값을 읽기만 하면 되는 경우 휘발성이 적합합니다.

입니다.volatile되는 변수(은 "da"가 "da"를 "da"를 "da"로 하는 시나리오 중 )volatile필수입니다).

// Code to prove importance of 'volatile' when state of one thread is being mutated from another thread.
// Try running this class with and without 'volatile' for 'state' property of Task class.
public class VolatileTest {
    public static void main(String[] a) throws Exception {
        Task task = new Task();
        new Thread(task).start();

        Thread.sleep(500);
        long stoppedOn = System.nanoTime();

        task.stop(); // -----> do this to stop the thread

        System.out.println("Stopping on: " + stoppedOn);
    }
}

class Task implements Runnable {
    // Try running with and without 'volatile' here
    private volatile boolean state = true;
    private int i = 0;

    public void stop() {
        state = false;
    } 

    @Override
    public void run() {
        while(state) {
            i++;
        }
        System.out.println(i + "> Stopped on: " + System.nanoTime());
    }
}

를 사용하지 않는 경우: "Stopping on: xxx" 후에도 "Stopped on: xxx" 메시지가 표시되지 않고 프로그램이 계속 실행됩니다.

Stopping on: 1895303906650500

사용 시: "Stopped on: xxx"가 즉시 표시됩니다.

Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300

데모: https://repl.it/repls/SilverAgonizingObjectcode

언급URL : https://stackoverflow.com/questions/106591/what-is-the-volatile-keyword-useful-for

반응형