휘발성 키워드가 유용한 이유


672

오늘 직장에서 저는 volatile키워드를 Java로 보았습니다. 그것에 익숙하지 않아서이 설명을 찾았습니다.

자바 이론 및 실습 : 변동성 관리

해당 기사에서 해당 키워드에 대해 자세히 설명 했으므로 키워드를 사용하거나 올바른 방식으로이 키워드를 사용할 수있는 사례를 볼 수 있습니까?

답변:


742

volatile메모리 가시성에 대한 의미가 있습니다. 기본적으로 volatile필드 값은 쓰기 작업이 완료된 후 모든 독자 (특히 다른 스레드)에게 표시됩니다. 이 없으면 volatile독자는 업데이트되지 않은 값을 볼 수 있습니다.

귀하의 질문에 대답하기 위해 : 예, volatile변수를 사용하여 일부 코드가 루프를 계속하는지 여부를 제어합니다. 루프는 volatile값을 테스트하고 값이 계속되면 계속합니다 true. false"stop"메소드를 호출하여 조건을 설정할 수 있습니다 . 루프는 falsestop 메소드가 실행을 완료 한 후 값을 테스트 할 때보고 종료합니다.

제가 추천하는 " 실제로 Java Concurrency "책에 대한 좋은 설명이 volatile있습니다. 이 책은 질문에서 언급 된 IBM 기사를 쓴 동일한 사람이 작성했습니다 (사실, 해당 기사의 맨 아래에 그의 책을 인용합니다). 필자가 사용하는 volatile기사는 "패턴 1 상태 플래그"라고합니다.

volatile후드 아래에서 작동 하는 방법에 대한 자세한 내용을 보려면 Java 메모리 모델을 읽으십시오 . 이 수준을 넘어서려면 Hennessy & Patterson 과 같은 훌륭한 컴퓨터 아키텍처 책을 확인하고 캐시 일관성 및 캐시 일관성에 대해 읽으십시오.


118
이 답변은 정확하지만 불완전합니다. volatile스레드가 volatile변수를 읽을 때 다른 스레드가 마지막으로 쓴 값뿐만 아니라 다른 변수에 대한 다른 모든 쓰기도 볼 수 있다는 점에서 JSR 133에 정의 된 새로운 Java 메모리 모델과 함께 제공 되는 중요한 특성이 생략 되었습니다. volatile쓰기 당시 다른 스레드에서 볼 수있었습니다 . 참조 이 답변 하고 이 참조를 .
Adam Zalcman

46
초보자를 위해 몇 가지 코드를 보여달라고 요청합니다 (제발?)
Hungry Blue Dev

6
질문에 링크 된 기사에는 코드 예제가 있습니다.
Greg Mattes

'Hennessy & Patterson'링크가 끊어진 것 같습니다. 그리고 'Java 메모리 모델'에 대한 링크는 실제로 Oracle의 Java 언어 사양 '제 17 장 스레드 및 잠금'으로 이어집니다.
Kris

2
@fefrei :“즉시”는 구어체 용어입니다. 물론, 실행 타이밍이나 스레드 스케줄링 알고리즘이 실제로 지정되어 있지 않으면 보장 할 수 없습니다. 프로그램이 휘발성 읽기가 특정 휘발성 쓰기에 후속하는지 여부를 알 수있는 유일한 방법은 표시된 값이 예상되는 값인지 확인하는 것입니다.
Holger

177

"… 휘발성 수정자는 필드를 읽는 스레드가 가장 최근에 작성된 값을 볼 수 있도록 보장합니다." -Josh Bloch

사용 volatile에 대해 생각하고 있다면 java.util.concurrent원자 행동을 다루는 패키지 를 읽으십시오 . 싱글 톤 패턴

에 대한 Wikipedia 게시물 은 사용 중입니다.


18
왜 모두가 volatilesynchronized키워드는?
ptkato

5
싱글 톤 패턴에 관한 Wikipedia 기사는 그 이후로 많이 바뀌었고 volatile더 이상 언급 된 예를 다루지 않습니다 . 보관 된 버전에서 찾을 수 있습니다 .
bskp

1
@ptkato이 두 키워드는 완전히 다른 목적을 제공하므로 동시성과 관련이 있지만 질문은 비교할만한 의미가 없습니다. 그것은 "왜 거기에 모두 말처럼 voidpublic키워드".
DavidS

134

에 대한 중요한 점 volatile:

  1. 자바의 동기화는 자바 키워드를 사용하여 가능 synchronized하고 volatile및 잠금.
  2. Java에서는 synchronized변수를 가질 수 없습니다 . 사용 synchronized변수와 키워드 불법 컴파일 오류가 발생합니다. synchronizedJava 에서 변수 를 사용하는 대신 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변수 volatile의 인스턴스를 생성하는 스레드 다음을 Singleton다른 스레드와 통신 할 수 없습니다. 따라서 스레드 A가 Singleton 인스턴스를 생성하고 생성 직후 CPU가 손상되면 다른 모든 스레드는 _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 컴파일러는 생성자가 실행을 마치기 전에 인스턴스에 대한 참조가 설정되는 방식으로 바이트 코드를 재 배열 할 수 있습니다. 이는 getInstance () 메소드가 완전히 초기화되지 않았을 수있는 오브젝트를 리턴 함을 의미합니다. 코드를 스레드로부터 안전하게 만들기 위해 인스턴스 변수에 Java 5부터 키워드 volatile을 사용할 수 있습니다. 휘발성으로 표시된 변수는 객체 생성자가 실행을 마치면 다른 스레드에서만 볼 수 있습니다.
출처

여기에 이미지 설명을 입력하십시오

volatileJava 사용법 :

페일 패스트 반복기는 일반적으로volatile 목록 객체 의 카운터를 사용하여 구현됩니다 .

  • 목록이 업데이트되면 카운터가 증가합니다.
  • 를 만들면 Iterator카운터의 현재 값이 Iterator개체에 포함됩니다 .
  • Iterator동작이 수행되고, 상기 방법은 두 개의 카운터 값을 비교하고,이 슬로우 ConcurrentModificationException가 다르면.

페일 세이프 반복기의 구현은 일반적으로 경량입니다. 이들은 일반적으로 특정 목록 구현 데이터 구조의 속성에 의존합니다. 일반적인 패턴은 없습니다.


2
"실패한 반복기는 일반적으로 휘발성 카운터를 사용하여 구현됩니다"-더 이상 너무 비용이 많이 들지 않음
Vsevolod Golovanov

_instance safe에 대한 이중 확인은 무엇입니까? 나는 그들이 휘발성으로 안전하지 않다고 생각했다
Dexters

"이것은 JVM 스레드가 주 메모리에서 휘발성 변수의 값을 읽고 로컬로 캐시하지 않도록 지시합니다." 좋은 지적
Humoyun Ahmad

스레드 안전성을 위해 함께 사용할 수도 private static final Singleton _instance;있습니다.
Chris311

53

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(). 휘발성이 없으면 루프를 실행하는 스레드가 변경 사항을 닫을 수 없습니다.

동기화가 필요없는 방법에 주목


2
왜 그것이 필요한지 궁금합니다. 스레드 동기화가 위험에 처한 방식으로 다른 스레드가이 스레드의 상태 변경에 반응해야하는 경우에만 필요하지 않습니까?
Jori

27
@Jori, while 루프에서 닫기 스레드 읽기가 close ()를 호출하는 스레드와 다르기 때문에 휘발성이 필요합니다. 휘발성이 없으면 루프를 실행하는 스레드가 변경 사항을 닫을 수 없습니다.
Pyrolistical

그런 스레드를 중지하거나 Thread # interrupt () 및 Thread # isInterrupted () 메소드를 사용하는 것 사이에 이점이 있다고 말 하시겠습니까?
Ricardo Belchior

2
@Pyrolistical-실 이 실제로 변화를 보지 않는 것을 관찰 했습니까 ? 또는 해당 문제를 확실하게 트리거하도록 예제를 확장 할 수 있습니까? 기본적으로 예제와 동일하지만 volatile키워드가 없는 코드를 사용하고 다른 코드를 사용하는 것을 알고 있기 때문에 궁금 합니다. 항상 잘 작동하는 것 같습니다.
aroth

2
@aroth : 오늘날의 JVM을 사용하면 실제로 가장 간단한 예제를 사용하더라도 실제로는 이 동작을 안정적으로 재현 할 수 없습니다 . 더 복잡한 응용 프로그램을 사용하면 코드 내에서 메모리 가시성을 보장하는 다른 작업이 작동하여 작동하게 만드는 이유가 무엇인지 알지 못하고 특히 코드의 단순하고 관련이없는 변경으로 인해 신청…
Holger

31

volatile사용하는 일반적인 예 는 volatile boolean변수를 플래그로 사용하여 스레드를 종료하는 것입니다. 스레드를 시작했는데 다른 스레드에서 안전하게 중단하려면 스레드가 주기적으로 플래그를 확인하도록 할 수 있습니다. 중지하려면 플래그를 true로 설정하십시오. 플래그를 작성하면 volatile검사중인 스레드가 다음 번에 synchronized블록을 사용할 필요없이 검사 할 때 설정되었음을 확인할 수 있습니다 .


27

volatile키워드로 선언 된 변수 에는 두 가지 주요 특성이있어 특별합니다.

  1. 휘발성 변수가 있으면 스레드가 컴퓨터의 (마이크로 프로세서) 캐시 메모리에 캐시 할 수 없습니다. 액세스는 항상 주 메모리에서 발생했습니다.

  2. 휘발성 변수에 대한 쓰기 작업 이 있고 갑자기 읽기 작업 이 요청되면 읽기 작업 이전에 쓰기 작업이 완료됩니다 .

위의 두 가지 특성으로 인해

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

반면에

  • 위에서 언급 한 # 2 를 자세히 조사하면 volatile키워드가 'n'개의 판독기 스레드와 하나의 기록기 스레드 만 있는 공유 변수를 유지하는 이상적인 방법 임을 알 수 있습니다 . volatile키워드를 추가하면 완료됩니다. 스레드 안전에 대한 다른 오버 헤드가 없습니다.

반대로

우리 volatile 키워드를 단독으로 사용할 수 없으며 , 액세스하는 작성자 스레드둘 이상인 공유 변수를 만족시킬 수 없습니다 .


3
이는 휘발성과 동기화의 차이점을 설명합니다.
ajay

13

long 및 double 변수 유형에 대한 읽기 및 쓰기 작업 처리에 대해서는 아무도 언급하지 않았습니다. 읽기 및 쓰기는 volatile 키워드를 원자 연산으로 사용해야하는 long 및 double 변수 유형을 제외하고 참조 변수 및 대부분의 원시 변수에 대한 원자 연산입니다. @링크


부울의 읽기 및 쓰기가 이미 원자이기 때문에 더 명확하게하기 위해 부울 휘발성을 설정할 필요가 없습니다.
Kai Wang

2
@KaiWang 원 자성 목적으로 부울에 휘발성을 사용할 필요가 없습니다. 그러나 당신은 분명히 가시성 이유로 할 수 있습니다. 그게 당신이 말하려는 것입니까?
SusanW

12

예, 가변 스레드가 여러 스레드에 의해 액세스되기를 원할 때마다 휘발성을 사용해야합니다. 일반적으로 단일 원자 작업 이상을 수행해야하므로 (예 : 변수 상태를 수정하기 전에 변수 상태 확인) 일반적으로 동기화 된 블록을 사용하기 때문에 일반적으로 사용되지 않습니다.


10

내 생각에, volatile 키워드가 사용되는 스레드 중지 이외의 두 가지 중요한 시나리오는 다음과 같습니다.

  1. 이중 점검 잠금 장치 . 싱글 톤 디자인 패턴에서 자주 사용됩니다. 여기에서 싱글 톤 객체는 휘발성으로 선언되어야 합니다.
  2. 가짜 모닝콜 . 통지 호출이 발행되지 않은 경우에도 스레드가 대기 호출에서 깨어날 수 있습니다. 이 동작을 가짜 웨이크 업이라고합니다. 조건부 변수 (부울 플래그)를 사용하여이 문제를 해결할 수 있습니다. 플래그가 true이면 wait () 호출을 while 루프에 넣으십시오. 따라서 Notify / NotifyAll 이외의 이유로 스레드가 대기 호출에서 깨어 난 경우 플래그가 여전히 발생하므로 호출이 다시 대기합니다. notify를 호출하기 전에이 플래그를 true로 설정하십시오. 이 경우 부울 플래그는 volatile로 선언됩니다 .

전체 # 2 섹션은 매우 혼란스러워 보이며 알림 손실, 가짜 깨우기 및 메모리 가시성 문제가 발생하고 있습니다. 또한 플래그의 모든 사용법이 동기화되어 있으면 휘발성이 중복됩니다. 나는 당신의 요점을 얻었지만 가짜 모닝콜은 올바른 용어가 아니라고 생각합니다. 명확히하십시오.
Nathan Hughes 19 :

5

다중 스레드 응용 프로그램을 개발하는 경우 '휘발성'키워드 또는 '동기화'및 기타 동시성 제어 도구 및 기술을 사용해야합니다. 이러한 응용 프로그램의 예로는 데스크톱 응용 프로그램이 있습니다.

응용 프로그램 서버 (Tomcat, JBoss AS, Glassfish 등)에 배포 할 응용 프로그램을 개발하는 경우 응용 프로그램 서버에서 이미 처리 한대로 동시성 제어를 직접 처리 할 필요는 없습니다. 실제로 Java EE 표준을 올바르게 기억하면 서블릿과 EJB에서 동시성 제어가 금지됩니다. 서블릿과 EJB는 처리가 필요없는 '인프라'계층의 일부이기 때문입니다. 싱글 톤 객체를 구현하는 경우 해당 앱에서만 동시성 제어를 수행합니다. Spring과 같은 프레임 워크를 사용하여 구성 요소를 짜는 경우에도 이미 해결되었습니다.

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


5

volatile모든 스레드가 자체적으로 증가하고 있음을 보장합니다. 예를 들어, 카운터는 변수의 동일한면을 동시에 봅니다. 동기화 또는 원자 또는 기타 항목 대신 사용되지 않으며 읽기를 완전히 동기화합니다. 다른 Java 키워드와 비교하지 마십시오. 아래 예제에서 볼 수 있듯이 휘발성 변수 연산도 원 자성이므로 한 번에 실패하거나 성공합니다.

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);
            }
        }
    }

4

예, 꽤 많이 사용합니다. 멀티 스레드 코드에 매우 유용 할 수 있습니다. 당신이 지적한 기사는 좋은 것입니다. 명심해야 할 두 가지 중요한 사항이 있지만

  1. 수행하는 작업과 동기화 된 것과의 차이점을 완전히 이해 한 경우에만 휘발성을 사용해야합니다. 휘발성에 대한 휘발성에 대한 더 나은 이해는 종종 휘발성에 대한 유일한 이해가 동기화가 작동 할 수있는 유일한 옵션 인 경우가 많을 때, 표면상에서 휘발성은 표면상에서 동기화에 대한 더 간단한 대안으로 보인다.
  2. volatile은 실제로 많은 오래된 JVM에서 작동하지 않지만 동기화되어 있습니다. 다른 JVM에서 다양한 수준의 지원을 참조한 문서를 본 것을 기억하지만 불행히도 지금은 찾을 수 없습니다. Java pre 1.5를 사용 중이거나 프로그램이 실행될 JVM을 제어 할 수없는 경우 확실히 살펴보십시오.

4

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

멤버 변수 만 일시적이거나 일시적 일 수 있습니다.


3

확실히 맞아요. (Java뿐만 아니라 C #에서도) 주어진 플랫폼, int 또는 boolean과 같은 원자 연산으로 보장되는 값을 가져 오거나 설정해야 할 때가 있지만 시간이 필요하지 않은 경우가 있습니다. 스레드 잠금 오버 헤드 volatile 키워드를 사용하면 값을 읽을 때 현재 값 을 가져 오고 다른 스레드에서 쓰기로 인해 사용되지 않는 캐시 된 값이 아닌 값 을 얻을 수 있습니다.


3

휘발성 키워드에는 두 가지 용도가 있습니다.

  1. JVM이 레지스터에서 값을 읽지 못하게하고 (캐시로 가정) 메모리에서 값을 읽습니다.
  2. 메모리 불일치 오류의 위험을 줄입니다.

JVM이 레지스터에서 값을 읽지 못하게하고 메모리에서 값을 읽습니다.

비지 플래그 장치가 비지하고 플래그가 로크에 의해 보호되지 않은 상태에서 계속 스레드를 방지하기 위해 사용된다 :

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

다른 스레드가 사용 중 플래그를 끄면 테스트 스레드가 계속됩니다 .

busy = 0;

그러나 busy는 테스트 스레드에서 자주 액세스되므로 JVM은 busy 값을 레지스터에 배치하여 테스트를 최적화 한 다음 모든 테스트 전에 메모리의 busy 값을 읽지 않고 레지스터의 내용을 테스트 할 수 있습니다. 테스트 스레드는 사용중 변경을 볼 수 없으며 다른 스레드는 메모리 사용중의 값만 변경하여 교착 상태가 발생합니다. busy 플래그 를 volatile로 선언하면 각 테스트 전에 값을 읽습니다.

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

휘발성 변수를 사용하면 휘발성 변수에 대한 쓰기 가 동일한 변수의 후속 읽기와 "일어남" 관계를 설정하기 때문에 메모리 일관성 오류 의 위험이 줄어 듭니다 . 이는 휘발성 변수에 대한 변경 사항이 항상 다른 스레드에 표시됨을 의미합니다.

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

원자 행동은 한 번에 효과적으로 발생하는 행동입니다. 원자 적 행동은 중간에 멈출 수 없다 : 그것은 완전히 일어나거나 전혀 일어나지 않는다. 조치가 완료 될 때까지 원자 조치의 부작용은 표시되지 않습니다.

다음은 원자적인 것으로 지정할 수있는 조치입니다.

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

건배!


3

volatile프로그래머에게는 값이 항상 최신이라고 말합니다. 문제는 다른 유형의 하드웨어 메모리에 값을 저장할 수 있다는 것입니다. 예를 들어 CPU 레지스터, CPU 캐시, RAM이 될 수 있습니다. СPU 레지스터 및 CPU 캐시는 CPU에 속하며 멀티 스레딩 환경에서 복구중인 RAM과 달리 데이터를 공유 할 수 없습니다.

여기에 이미지 설명을 입력하십시오

volatile키워드는 변수가 RAM 메모리에서 직접 RAM 메모리 로 읽히고 쓰여질 것이라고 말합니다 . 계산 공간이 약간 있습니다

Java 5[정보]volatile 를 지원하여 확장happens-before

휘발성 필드에 대한 쓰기는 해당 필드를 읽을 때마다 발생합니다.

volatile여러 스레드가 동시에 일부 값을 수 있는 경우 키워드 race condition 상황을 치료하지 않습니다 . 대답은 키워드입니다 [정보]synchronized

결과적으로 하나의 스레드가 쓰이고 다른 스레드 가 volatile값을 읽을 때만 안전 합니다.

휘발성 대 동기화


2

휘발성은 다음과 같습니다.

1> 다른 스레드에 의한 휘발성 변수 읽기 및 쓰기는 스레드 자체 캐시 또는 CPU 레지스터가 아닌 항상 메모리에서 가져옵니다. 따라서 각 스레드는 항상 최신 값을 처리합니다. 2> 두 개의 다른 스레드가 동일한 인스턴스 또는 정적 변수를 힙으로 사용하면 다른 작업이 순서대로 잘못 표시 될 수 있습니다. 이에 대한 제레미 맨슨의 블로그를 참조하십시오. 그러나 휘발성이 도움이됩니다.

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

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


1

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

휘발성 변수를 사용하면 휘발성 변수에 대한 쓰기가 동일한 변수의 후속 읽기와의 관계를 확립하기 때문에 메모리 일관성 오류의 위험이 줄어 듭니다.

즉, volatile변수 변경 사항 은 항상 다른 스레드에 표시됩니다. 또한 스레드가 휘발성 변수를 읽을 때의 최신 변경 사항 volatile뿐만 아니라 변경 의 원인이 된 코드의 부작용도 확인합니다.

Peter Parker답변에서 설명한 것처럼 volatile수정자가 없으면 각 스레드의 스택에는 고유 한 변수 사본이있을 수 있습니다. 변수를로 설정 volatile하면 메모리 일관성 문제가 해결되었습니다.

이해를 돕기 위해 jenkov 튜토리얼 페이지를 살펴보십시오 .

휘발성을 사용하는 휘발성 및 사용 사례에 대한 자세한 내용은 관련 SE 질문을 살펴보십시오.

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

실제 사용 사례 :

현재 시간을 특정 형식으로 인쇄해야하는 스레드가 많이 java.text.SimpleDateFormat("HH-mm-ss")있습니다. Yon은 현재 시간을 변환 SimpleDateFormat하고 1 초마다 변수를 업데이트하는 하나의 클래스를 가질 수 있습니다 . 다른 모든 스레드는이 휘발성 변수를 사용하여 현재 시간을 로그 파일로 인쇄 할 수 있습니다.


1

휘발성 변수는 경량 동기화입니다. 모든 스레드에서 최신 데이터의 가시성이 요구되고 원 자성이 손상 될 수있는 경우, 이러한 상황에서 휘발성 변수가 선호되어야합니다. 휘발성 변수에 대한 읽기는 레지스터 나 다른 프로세서가 볼 수없는 캐시에 캐시되지 않기 때문에 스레드에 의해 수행 된 가장 최근의 쓰기를 항상 반환합니다. 휘발성은 잠금이 없습니다. 시나리오가 위에서 언급 한 기준을 충족 할 때 휘발성을 사용합니다.


-1

변수와 함께 사용될 때 휘발성 키는이 변수를 읽는 스레드가 동일한 값을 보게합니다. 변수를 읽고 쓰는 스레드가 여러 개인 경우 변수를 휘발성으로 만드는 것만으로는 충분하지 않으며 데이터가 손상됩니다. 이미지 쓰레드는 같은 값을 읽었지만, 각각의 쓰레드는 약간의 chage (카운터 증가)를 수행하여 메모리에 다시 쓸 때 데이터 무결성이 위반됩니다. 그래서 varible을 동기화해야합니다 (다른 방법이 가능합니다)

하나의 스레드로 변경이 이루어지고 다른 스레드가이 값을 읽어야하는 경우 휘발성이 적합합니다.


-1

휘발성 변수는 기본적으로 업데이트되면 기본 공유 캐시 라인에서 즉시 업데이트 (플러시)에 사용되므로 변경 사항이 모든 작업자 스레드에 즉시 반영됩니다.


-2

아래는 volatile다른 스레드에서 스레드 실행을 제어하는 ​​데 사용되는 변수 에 대한 요구 사항을 보여주는 매우 간단한 코드 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());
    }
}

volatile사용되지 않는 : 당신은 '볼 수 없을 것입니다 : XXX에 중지 도'후 메시지 ' 에 중지 : XXX ', 프로그램이 계속 실행됩니다.

Stopping on: 1895303906650500

volatile사용 : 당신은 '볼 수 있습니다 : XXX에 정지 즉시'.

Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300

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


downvoter로 : downvote를 왜 설명해야합니까? 이것이 사실이 아닌 경우 적어도 나는 무엇이 잘못되었는지 배울 것입니다. 이 같은 의견을 두 번 추가했지만 누가 또 다시 삭제하는지 모릅니다
manikanta

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.