2016-08-07 8 views
7

Я изучаю Java тему, и ключевое слово volatile меня смущает, когда я анализирую следующий код модифицированный из Example 8.3.1.4Как использовать летучие правильно в Java

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

выводного сегмент:

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 

Моих что из-за volatilei ++ происходит до j ++, их начальные значения равны нулю, и измененные значения будут немедленно сброшены в основную память, поэтому в любое время i нить t2 видно должно быть больше j. Однако вывод показывает, что i всегда ниже j.

Тогда я изменить функцию two следующим образом:

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

Изменение в том, что j выходы до i, то выходной сегмент следующим образом:

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

Это меня удивляет, что j всегда ниже i.

Моя мысль в том, что j ниже, потому что это связано в первую и через некоторое время i связано, в течение временного промежутка функция one выполняющей, которая вызывает i увеличивается.

Итак, первое подключенное значение будет ниже второго подключенного, верно? Заранее спасибо!

+0

Не забудьте принять ответы на свои вопросы или запросить разъяснения. Подробнее: [* Что делать, если кто-то отвечает на мой вопрос? *] (/ Help/someone-answers) –

ответ

7

Вы предполагаете, что правильно. Разница исходит из того, что аргумент println вызова построен в 3 этапа:

  1. построить строку s = «я =» + я
  2. построить Строка S = S + «J =»
  3. построить строку, S = S + J

и в то время, когда Runnable2 делает это, особенно после того, как step1 и до заключительной печати, Runnable2 занят приращением значения. И это приводит к поведению, которое вы видите.

Это не проблема волатильности. Если вы хотите синхронизировать i и j, вы должны синхронизировать методы класса Test1.

3

На самом деле, volatile просто предотвращает тему кэшировать значения, но вместо того, чтобы принуждать «сквозную запись» и «чтений-Through»: Если переменный кэшируются одним потоком и обновляются другим, значение может никогда не изменится для первого потока, потому что из-за политики кэширования Java не требуется обновлять свои кеши.

Таким образом, всякий раз, когда у вас есть несколько потоков, которые обращаются к первому ресурсу и изменяют его (например, int, boolean или ссылочное значение ссылочного типа), вы должны использовать volatile.

Что само по себе не означает, что волатильность фактически делает переменный доступ потокобезопасным!