简体   繁体   English

参考更新线程安全吗?

[英]Is reference update thread safe?

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


}

Updated regularly updates myobj更新定期更新myobj
Other classes fetch data using getData其他类使用 getData 获取数据
IS this code thread safe without using volatile keyword?在不使用 volatile 关键字的情况下,此代码线程安全吗?
I think yes.我想是的。 Can someone confirm?有人可以确认吗?

No , this is not thread safe.,这不是线程安全的。 (What makes you think it is?) (是什么让你认为它是?)

If you are updating a variable in one thread and reading it from another, you must establish a happens-before relationship between the write and the subsequent read.如果您在一个线程中更新变量并从另一个线程读取它,则必须在写入和后续读取之间建立一个发生在之前的关系

In short, this basically means making both the read and write synchronized (on the same monitor), or making the reference volatile .简而言之,这基本上意味着使读取和写入synchronized (在同一监视器上),或使引用volatile

Without that, there are no guarantees that the reading thread will see the update - and it wouldn't even be as simple as "well, it would either see the old value or the new value".没有它,就不能保证读取线程会看到更新——它甚至不会像“好吧,它会看到旧值或新值”那么简单。 Your reader threads could see some very odd behaviour with the data corruption that would ensue.您的读者线程可能会看到一些非常奇怪的行为以及随之而来的数据损坏。 Look at how lack of synchronization can cause infinite loops , for example (the comments to that article, especially Brian Goetz', are well worth reading):例如,看看缺乏同步如何导致无限循环(该文章的评论,尤其是 Brian Goetz 的评论非常值得一读):

The moral of the story: whenever mutable data is shared across threads, if you don't use synchronization properly (which means using a common lock to guard every access to the shared variables, read or write), your program is broken, and broken in ways you probably can't even enumerate.这个故事的寓意是:每当跨线程共享可变数据时,如果您没有正确使用同步(这意味着使用公共锁来保护对共享变量的每次访问,读或写),您的程序就会被破坏,被破坏以你可能无法列举的方式。

No, it isn't.不,不是。

Without volatile , calling getData() from a different thread may return a stale cached value.如果没有volatile ,从不同的线程调用getData()可能会返回一个陈旧的缓存值。
volatile forces assignments from one thread to be visible on all other threads immediately. volatile强制来自一个线程的分配立即在所有其他线程上可见。

Note that if the object itself is not immutable, you are likely to have other problems.请注意,如果对象本身不是不可变的,则您可能会遇到其他问题。

You may get a stale reference.可能会得到一个陈旧的参考。 You may not get an invalid reference.可能不会得到无效的参考。 The reference you get is the value of the reference to an object that the variable points to or pointed to or will point to.您获得的引用是对变量指向或指向或将指向的对象的引用的值。

Note that there are no guarantees how much stale the reference may be, but it's still a reference to some object and that object still exists .需要注意的是没有保证的引用可能多少会过时,但它仍然是一些对象的引用和对象仍然存在 In other words, writing a reference is atomic (nothing can happen during the write) but not synchronized (it is subject to instruction reordering, thread-local cache et al.).换句话说,写入引用是原子的(在写入过程中不会发生任何事情)但不是同步的(它受指令重新排序、线程本地缓存等的影响)。

If you declare the reference as volatile , you create a synchronization point around the variable.如果将引用声明为volatile ,则会在变量周围创建一个同步点。 Simply speaking, that means that all cache of the accessing thread is flushed (writes are written and reads are forgotten).简单来说,这意味着访问线程的所有缓存都被刷新(写入被写入,读取被遗忘)。

The only types that don't get atomic reads/writes are long and double because they are larger than 32-bits on 32-bit machines.唯一不能进行原子读/写的类型是longdouble因为它们在 32 位机器上大于 32 位。

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

The big problem with this sort of code is the lazy initialization.这种代码的大问题是延迟初始化。 Without volatile or synchronized keywords, you could assign a new value to myobj that had not been fully initialized.如果没有volatilesynchronized关键字,您可以为尚未完全初始化的myobj分配一个新值。 The Java memory model allows for part of an object construction to be executed after the object constructor has returned. Java 内存模型允许在对象构造函数返回执行对象构造的一部分。 This re-ordering of memory operations is why the memory-barrier is so critical in multi-threaded situations.内存操作的这种重新排序就是内存屏障在多线程情况下如此重要的原因。

Without a memory-barrier limitation, there is no happens-before guarantee so you do not know if the MyObj has been fully constructed.没有内存屏障限制,就没有发生前保证,因此您不知道MyObj是否已完全构建。 This means that another thread could be using a partially initialized object with unexpected results.这意味着另一个线程可能正在使用部分初始化的对象,结果出乎意料。

Here are some more details around constructor synchronization:以下是有关构造函数同步的更多详细信息:

Constructor synchronization in Java Java 中的构造函数同步

Volatile would work for boolean variables but not for references. Volatile 适用于布尔变量,但不适用于引用。 Myobj seems to perform like a cached object it could work with an AtomicReference. Myobj 似乎表现得像一个缓存对象,它可以与 AtomicReference 一起工作。 Since your code extracts the value from the DB I'll let the code stay as is and add the AtomicReference to it.由于您的代码从 DB 中提取值,我将让代码保持原样并向其添加 AtomicReference。

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 {
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM