1
public class Test{
   private MyObj myobj = new MyObj(); //it is not volatile


   public class Updater extends Thred{
      myobje = getNewObjFromDb() ; //not am setting new object
   }

   public MyObj getData(){
    //getting stale date is fine for 
    return myobj;
   }


}

定期更新更新 myobj
其他类使用 getData 获取数据
此代码是否线程安全而不使用 volatile 关键字?
我想是的。有人可以确认吗?

4

6 回答 6

7

,这不是线程安全的。(是什么让你认为它是?)

如果您在一个线程中更新一个变量并从另一个线程读取它,您必须在写入和后续读取之间建立发生前的关系。

简而言之,这基本上意味着同时进行读取和写入synchronized(在同一监视器上),或进行引用volatile

没有它,就不能保证读取线程会看到更新 - 它甚至不会像“嗯,它会看到旧值或新值”那么简单。您的阅读器线程可能会看到一些非常奇怪的行为以及随之而来的数据损坏。例如,看看缺乏同步如何导致无限循环(该文章的评论,尤其是 Brian Goetz 的评论非常值得一读):

故事的寓意:每当跨线程共享可变数据时,如果您没有正确使用同步(这意味着使用公共锁来保护对共享变量的每次访问,无论是读取还是写入),您的程序都会被破坏和破坏以你可能无法列举的方式。

于 2012-10-23T14:32:06.777 回答
1

可能会得到一个过时的参考。您可能不会得到无效的参考。你得到的引用是对变量指向或指向或将指向的对象的引用的值。

请注意,无法保证引用可能有多少陈旧,但它仍然是对某个对象的引用,并且该对象仍然存在。换句话说,写入引用是原子的(在写入期间不会发生任何事情)但不是同步的(它受指令重新排序、线程本地缓存等的影响)。

如果将引用声明为volatile,则会在变量周围创建一个同步点。简单地说,这意味着访问线程的所有缓存都被刷新(写入被写入,读取被遗忘)。

唯一没有原子读/写的类型是long并且double因为它们在 32 位机器上大于 32 位。

于 2012-10-23T14:48:49.970 回答
1

不,不是。

如果没有,从不同的线程volatile调用可能会返回一个过时的缓存值。强制来自一个线程的分配立即在所有其他线程上可见。getData()
volatile

请注意,如果对象本身不是不可变的,您可能会遇到其他问题。

于 2012-10-23T14:30:56.567 回答
1

如果MyObj是不可变的(所有字段都是最终的),则不需要 volatile。

于 2012-10-24T05:41:27.297 回答
0

Volatile would work for boolean variables but not for references. Myobj seems to perform like a cached object it could work with an AtomicReference. Since your code extracts the value from the DB I'll let the code stay as is and add the AtomicReference to it.

import java.util.concurrent.atomic.AtomicReference;

    public class AtomicReferenceTest {
        private AtomicReference<MyObj> myobj = new AtomicReference<MyObj>();

        public class Updater extends Thread {

            public void run() {
                MyObj newMyobj = getNewObjFromDb();
                updateMyObj(newMyobj);
            }

            public void updateMyObj(MyObj newMyobj) {
                myobj.compareAndSet(myobj.get(), newMyobj);
            }

             }
        public MyObj getData() {
             return myobj.get();
        }
    }

    class MyObj {
    }
于 2012-10-24T17:20:46.583 回答
0

这种代码的大问题是延迟初始化。如果没有volatileorsynchronized关键字,您可以为myobj尚未完全初始化的新值分配一个新值。Java 内存模型允许在对象构造函数返回执行部分对象构造。这种内存操作的重新排序是内存屏障在多线程情况下如此重要的原因。

没有内存屏障限制,就没有发生之前的保证,因此您不知道是否MyObj已完全构建。这意味着另一个线程可能正在使用部分初始化的对象并产生意外结果。

以下是有关构造函数同步的更多详细信息:

Java中的构造函数同步

于 2012-10-23T15:05:32.837 回答