1 Kasım 2018 Perşembe

volatile Anahtar Kelimesi

Volatile
Volatile C++ ve Java'da farklı manalara gelir. Java ve C#'ta volatile kelimesi multithreading için kullanılır. Başka bir yerde işe yaramaz. Açıklaması şöyle.
volatile keyword guarantees visibility of shared state among multiple threads whenever there is a change.
Java'da çalışma prensibini açıklayan bir yazı burada.

Yazma İşlemi
Yazma işlemini tek bir thread'in yapması doğrudur. Açıklaması şöyle.
volatile keyword works well if there is only one writing thread.
Eğer iki thread aynı anda yazma yapıyorsa bir tanesinin yazdığı ezilir. Açıklaması şöyle.
If there are 2 or more writing threads, race condition will happen: all writing threads get the latest version of variable, modify value at its own CPU, then write to main memory. The consequence is that data in memory is just the output of one thread, other threads' modification were overridden.
Volatile bir değişkene yazma işlemi gerçekleşince diğer tüm thread'lerin en son değeri göreceği garanti edilir. Bunun nasıl gerçekleşeceği ise açık değil.

Bariyer kullanarak şöyle yapılır. Her yazma işleminden sonra bir store bariyeri kullanılır. Böylece yazılan değeri diğer işlemciler de görebilirler.

Okuma İşlemi
Bariyer kullanarak şöyle yapılır. Her okuma işleminden önce bir load (acquire) bariyeri kullanılır. Böylece diğer işlemcilerin yazdıkları değerler görülebilir.

Önce Okuma Sonra Yazma Varsa
Bu durumda volatile kullanılamaz. Açıklaması şöyle.
- if you have a field and you want to make sure that if one thread writes to it, other threads can immediately read the written value - volatile is enough (without volatile, other threads may never even see the written value)

- if you have a field that you need to first read and only then write (based on the value you just read so no operations on this value may happen in between), volatile is not enough; instead, you can use:
Volatile ve Final
Volatile ve final mantık olarak bir araya gelemeyeceği için derleyici hata verir. final değerin değişmeyeceğini belirtir. Volatile ise değişebileceğini belirtir. Dolayısıyla çelişirler.
private final volatile AtomicInteger size; //1, Not compile
Örnek
Volatile int kullanarak "double buffering" yapılabilir. İki tane volatile int tanımlanır
volatile int buffer = 0;
volatile int process = 1;
İki tane de kuyruk tanımlanır
Queue<Foo> [] doubleQueues = ...;
Yazan taraf şöyle yazar
doubleQueues [buffer].add (...)
İşleyen taraf şöyle yapar ve önce dolu kuyruğun tamamını işler.
doubleQueues [process].poll (...)
Daha sonra işleyen taraf işi bitince kuyrukları değiştirir
buffer ^= 1; //buffer 1 olur ve yazan taraf boş kuyruğa geçer
process ^= 1;//process 0 olur ve işleyen taraf dolu kuyruğa geçer
Örnek
Şöyle yaparız. ConcurrentHashSet değişkeninin kendisi yeniden yaratılınca volatile olduğu için diğer thread'ler de yeni değişkeni görebilir.
public class Test {

  private volatile Set<Integer> cache = Sets.newConcurrentHashSet();

  // multiple threads call this
  public boolean contain(int num) {
    return cache.contains(num);
  }

  // background thread periodically calls this
  private void refresh() {
    Set<Integer> newSet = getNums();
    cache = newSet; // is this assignment thread safe?
  }

  private Set<Integer> getNums() {
    Set<Integer> newSet = Sets.newConcurrentHashSet();
    // read numbers from file and add them to newSet
    return newSet;
  }
}


Hiç yorum yok:

Yorum Gönder