2010-04-15 4 views
3

Как я понимаю, если мы объявим переменную как volatile, то она не будет сохранена в локальном кеше. Всякий раз, когда поток обновляет значения, он обновляется до основной памяти. Таким образом, другие потоки могут получить доступ к обновленному значению.using volatile ключевое слово

Но в следующей программе отображаются как изменчивые, так и энергонезависимые переменные.

Переменная volatile не обновляется для второго потока. Может ли кто-нибудь объяснить это, почему testValue не изменен.

class ExampleThread extends Thread { 
    private int testValue1; 
    private volatile int testValue; 
    public ExampleThread(String str){ 
     super(str); 
    } 
    public void run() { 
    if (getName().equals("Thread 1 ")) 
    { 
     testValue = 10; 
     testValue1= 10; 
     System.out.println("Thread 1 testValue1 : " + testValue1); 
     System.out.println("Thread 1 testValue : " + testValue); 
    } 
    if (getName().equals("Thread 2 ")) 
    { 
     System.out.println("Thread 2 testValue1 : " + testValue1); 
     System.out.println("Thread 2 testValue : " + testValue); 
    }    
} 
} 

public class VolatileExample { 
    public static void main(String args[]) { 
     new ExampleThread("Thread 1 ").start(); 
     new ExampleThread("Thread 2 ").start(); 
    } 
} 


output: 
Thread 1 testValue1 : 10 
Thread 1 testValue : 10 
Thread 2 testValue1 : 0 
Thread 2 testValue : 0 

ответ

1

testValue является переменной-члена, так что две нити видеть две независимые копии. volatile имеет значение, если два или более потоков имеют ссылку на один и тот же объект.

Сделать testValue статическим и неустойчивым будет иметь эффект. Однако вы не можете (и, вероятно, не будете) видеть этот эффект, так как он сильно зависит от стратегий времени, планирования и кэширования, которые не соответствуют вашему (или даже виртуальному) управлению. Отсутствие волатильности будет редко иметь эффект, что делает такие ошибки очень трудными для улавливания. Это будет видно только тогда, когда поток обновляет значение, а второй поток считывает значение, и значение все еще находится в кеше в любом из двух потоков.

+0

Это фактически переменная члена, а не локальная. См. Ответ полигена. –

+0

может у plz объяснить с примером ... я не получаю – sap

+0

@Steven вы правы. Обновлен мой ответ. @sap увидеть мой обновленный ответ –

1

Возможно, вы утеряли статическое ключевое слово?

+0

я спрашиваю о изменчивы, не статичны ... Статическая любом случае отлично работает с или без летучего – sap

7

Ваши переменные ограничены одним потоком, поэтому нет другого потока, к которому они обращаются. Таким образом, volatile не имеет значения.

Если вы объявили их static, они будут доступны для разных потоков. Однако даже тогда вы не сможете наблюдать разницу между вашей изменчивой и энергонезависимой переменной. Цитата из Java Concurrency in Practice, гл. 3.1.4:

Эффекты видимости изменчивых переменных распространяются вне значения самой изменчивой переменной. Когда поток A записывает в изменчивую переменную, а затем поток B считывает эту же переменную, значения всех переменных, которые были видны A перед записью в изменчивую переменную, становятся видимыми B после прочтения изменчивой переменной. Таким образом, с точки зрения видимости памяти, запись изменчивой переменной похожа на выход из синхронизированного блока, и чтение изменчивой переменной похоже на ввод синхронизированного блока.

В вашем случае код сначала изменяет изменчивую переменную, поэтому обновленное значение другой переменной может быть не видимым для другого потока. Все идет нормально.

Однако, поскольку вы распечатываете значения переменных из той же темы, которая их модифицировала, вы все равно не увидите никакой разницы.

Update2: Попробуйте эту измененную версию (примечание: я не проверял):

class ExampleThread extends Thread { 
    private static int testValue1; 
    private static volatile int testValue; 
    private int newValue; 

    public ExampleThread(String str, int newValue){ 
     super(str); 
     this.newValue = newValue; 
    } 
    public void run() { 
     for (int i = 0; i < 10; i++) { 
     System.out.println(getName() + " testValue1 before update: " + testValue1); 
     System.out.println(getName() + " testValue before update: " + testValue); 
     testValue = i * newValue; 
     testValue1 = i * newValue; 
     System.out.println(getName() + " testValue1 after update: " + testValue1); 
     System.out.println(getName() + " testValue after update: " + testValue); 
     sleep(10); 
     }    
    }    
} 

public class VolatileExample { 
    public static void main(String args[]) { 
     new ExampleThread("Thread 1 ", 5).start(); 
     new ExampleThread("Thread 2 ", 10).start(); 
    } 
} 

Update: относительно видимости статических полей - опять-таки из того же тома (глава 16.2.3):

[...] Статически инициализированные объекты не требуют явной синхронизации ни во время строительства, ни при ссылках. Однако это относится только к состоянию как сконструированное состояние - если объект изменен, синхронизация по-прежнему требуется как читателям, так и писателям, чтобы сделать последующие изменения видимыми и избежать повреждения данных.

+0

я спрашиваю о изменчивы, не статичны ... Статическая любом случае отлично работает с или без летучих – sap

+0

Вы можете уточнить, что ваша фраза «ограничивается одним потоком» относится к экземпляру Thread, а не к самому потоку (к которому мы можем ограничить переменную с помощью ThreadLocal). –

+0

@Steven Mackenzie В приведенном выше примере переменные, о которых идет речь, являются частными членами, доступ к которым может получить только их владелец (который является экземпляром Thread). Поэтому они явно ограничены потоком, связанным с этим экземпляром. ThreadLocal - это не единственный способ добиться ограничения потока. –

2

ExampleThread 1 и ExampleThread 2 - разные объекты.

В одном из них вы назначили 10 для обоих полей int, и именно поэтому вы видите этот вывод для первого потока.

Во-вторых, вы не назначили ничего для полей int, чтобы получить 0s.

+0

может усовершенствовать для того, как мы можем реализовать его для volatile. – sap

3

Это не имеет никакого отношения к volatile; это два отдельных экземпляра ExampleThread со своими собственными копиями testValue1 и testValue, которые представляют собой поля экземпляра (но не static переменных класса, которые являются «разделяемыми» между всеми экземплярами).

+0

Может ли PLZ объяснить об изменчивой функциональности? – sap

+0

Вы должны прочитать на Java Memory Model и книгу «Java Concurrency in Practice». – polygenelubricants

1

Вот пример, показывающий переменную, к которой обращаются два потока. Нить StarterThread задает переменную started при запуске нити. WaiterThread ждет переменную started.

public class Main 
{ 
    static /*volatile*/ boolean started = false; 

    private static class StarterThread extends Thread 
    { 
     public void run() 
     { 
      started = true; 
     } 
    } 

    private static class WaiterThread extends Thread 
    { 
     public void run() 
     { 
      while (!started) 
      { 
      } 
     } 
    } 

    public static void main(String[] args) 
    { 
     new StarterThread().start(); 
     new WaiterThread().start(); 
    } 
} 

Если started не летучий, то нет точки синхронизации, которая гарантирует, что WaiterThread будет когда-либо получить обновленное значение переменной started. Следовательно, поток WaiterThread может потенциально запускаться «бесконечно».

Смежные вопросы