2016-08-07 18 views
7

uczę wątek Java, a kluczowe volatile myli mnie, kiedy analizować następujący kod modyfikowany Example 8.3.1.4Jak używać lotnych poprawnie w Javie

public class Volatile { 
    public static void main(String[] args){ 
     MyRunnable1 myRunnable1 = new MyRunnable1(); 
     MyRunnable2 myRunnable2 = new MyRunnable2(); 
     Thread t1 = new Thread(myRunnable1); 
     Thread t2 = new Thread(myRunnable2); 

     t1.start(); 
     t2.start(); 
    } 
} 

class MyRunnable1 implements Runnable{ 
    public void run(){ 
     while(true){ 
      Test1.one(); 
     } 
    } 
} 
class MyRunnable2 implements Runnable{ 
    public void run(){ 
     while(true){ 
      Test1.two(); 
     } 
    } 
} 

class Test1{ 
    static volatile int i = 0; 
    static volatile int j = 0; 
    static void one(){ 
     i ++; 
     j ++; 
    } 

    static void two(){ 
     System.out.println("i = " + i + " j = " + j); 
    } 
} 

An segmentu wyjściowego:

i = 60778110 j = 60778116 
i = 60778402 j = 60778407 
i = 60778630 j = 60778636 
i = 60779062 j = 60779079 
i = 60779492 j = 60779497 
i = 60779784 j = 60779789 
i = 60780161 j = 60780169 
i = 60780625 j = 60780632 
i = 60780936 j = 60780942 

My uważa się, że z powodu volatile dzieje się przed ich wartości początkowe są zerowe i zmodyfikowane wartości zostaną natychmiast przepłukane do pamięci głównej, więc w dowolnym momencie i wątek t2 Widoki powinny być większe niż j. Jednak dane wyjściowe pokazują, że i jest zawsze niższy niż j.

Potem zmodyfikować funkcję two następująco:

static void two(){ 
    System.out.println("j = " + j + " i = " + i); 
} 

Zmiana jest j wyjść przed i, następnie wyjście segmentu następująco:

j = 47324409 i = 47324412 
j = 47324587 i = 47324593 
j = 47324808 i = 47324813 
j = 47324991 i = 47324996 
j = 47325193 i = 47325196 
j = 47325347 i = 47325353 

Zaskakuje mnie, że j jest zawsze niższy niż i.

Moja myśl jest, że j jest niższa, ponieważ jest podłączony pierwszy i po chwili i jest podłączony, podczas szczeliny czasowej funkcja one wykonuje co powoduje i wzrosła.

Tak więc pierwsza podłączona wartość będzie niższa niż druga podłączona, czy to prawda? Z góry dziękuję!

+0

Nie zapomnij przyjąć odpowiedzi na swoje pytania lub poprosić o wyjaśnienia. Więcej: [* Co powinienem zrobić, gdy ktoś odpowiada na moje pytanie? *] (/ Help/someone-answers) –

Odpowiedz

7

Masz rację. Różnica wynika z faktu, że argument na wezwanie println jest zbudowany w 3 krokach:

  1. zbudować ciąg s = "i =" + i
  2. zbudować łańcuch s = s + "j ="
  3. zbudować łańcuch s = s + J

aw momencie Runnable2 to robi, zwłaszcza po kroku 1, a przed ostatecznym drukiem, Runnable2 jest zajęty zwiększając wartości. A to prowadzi do zachowania, które widzisz.

To nie jest problem niestabilności. Jeśli chcesz, aby ja i j były zsynchronizowane, musisz zsynchronizować metody klasy Test1.

3

Rzeczywiście, po prostu uniemożliwia wątkom buforowanie wartości, ale zamiast tego wymusza "przepuszczanie" i "odczyt": Jeśli zmienna jest buforowana przez jeden wątek i aktualizowana przez inny, wartość może nigdy się nie zmienić dla pierwszego wątku, ponieważ ze względu na zasady buforowania Java nie jest wymagane odświeżanie jego pamięci podręcznych.

Tak więc za każdym razem, gdy masz wiele wątków uzyskujących dostęp i zmieniających podstawowe zasoby (takie jak int, boolean lub sama wartość odniesienia typu reference), powinieneś użyć volatile.

Co samo w sobie nie oznacza, że ​​lotny rzeczywiście sprawia, że ​​zmienny dostęp jest bezpieczny dla wątków!