java java中带有volatile的单例
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15077910/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
singleton with volatile in java
提问by Trying
class MyClass
{
private static volatile Resource resource;
public static Resource getInstance()
{
if(resource == null)
resource = new Resource();
return resource;
}
}
Here my doubt is according to java concurrency in practice if you use volatile, safe publication happens (i.e. as soon the reference is visible to another thread the data is also available). So can I use it here? But if it is correct then suppose thread1 now checks "resource" and it's null so it starts creating the object. While thread1 is creating the objet another thread i.e. thread2 comes and start checking the value of "resource" and thread2 finds it as null (assume creating "resource" object takes some considerable amount of time and as thread1 has not yet completed the creation so the safe publication hasn't happened hence unavailable to thread2 )then will it also start creating the object? if yes then class invariant breaks. Am I correct? Please help me in understanding this specially use of volatile here.
在这里,我的疑问是根据实践中的 java 并发,如果您使用 volatile,则会发生安全发布(即,只要引用对另一个线程可见,数据也可用)。那么我可以在这里使用它吗?但是如果它是正确的,那么假设 thread1 现在检查“资源”并且它是空的,所以它开始创建对象。当线程 1 正在创建对象时,另一个线程即线程 2 来并开始检查“资源”的值,线程 2 发现它为空(假设创建“资源”对象需要相当长的时间,并且线程 1 尚未完成创建,因此安全发布尚未发生,因此线程 2 不可用)那么它是否也会开始创建对象?如果是,则类不变量会中断。我对么?请帮助我理解这里对 volatile 的特殊使用。
回答by corsiKa
You are correct, multiple threads could try to create a Resource object. Volatile just guarantees that if one thread updates the reference, all other threads will see the new reference, not some cached reference. This is slower, but safer.
你是对的,多个线程可以尝试创建一个 Resource 对象。Volatile 只是保证如果一个线程更新引用,所有其他线程都会看到新的引用,而不是一些缓存的引用。这速度较慢,但更安全。
If you require only a single resource that is lazy loaded, you need to do something like this:
如果您只需要一个延迟加载的资源,则需要执行以下操作:
class MyClass
{
private static volatile Resource resource;
private static final Object LOCK = new Object();
public static Resource getInstance()
{
if(resource == null) {
synchronized(LOCK) { // Add a synch block
if(resource == null) { // verify some other synch block didn't
// write a resource yet...
resource = new Resource();
}
}
}
return resource;
}
}
回答by corsiKa
volatile solves one issue that is visibility issue. If you are writing to one variable that is declared volatilethen the value will be visible to other thread immediately. As we all know we have different level of cache in os L1, L2, L3 and if we write to a variable in one thread it is not guaranteed to be visible to other, so if we use volatile it writes to direct memory and is visible to others. But volatile does not solve the issue of atomicityi.e. int a; a++;
is not safe. AS there are three machine instructions associated to it.
volatile 解决了一个问题,即可见性问题。如果您正在写入一个声明为 volatile 的变量,那么该值将立即对其他线程可见。众所周知,我们在 os L1、L2、L3 中有不同级别的缓存,如果我们在一个线程中写入一个变量,它不能保证对其他线程可见,所以如果我们使用 volatile,它会写入直接内存并且是可见的给别人。但是 volatile 并没有解决原子性的问题,即int a; a++;
不安全。因为有三个与之相关的机器指令。
回答by John Vint
I know you aren't asking about better solutions but this is definitely worth if you are looking for a lazy singleton solution.
我知道你不是在问更好的解决方案,但如果你正在寻找一个懒惰的单例解决方案,这绝对是值得的。
Use a private static class to load the singleton. The class isn't loaded until invocation and so the reference isn't loaded until the class is. Class loading by implementation is thread-safe and you also incur very little overhead (in case you are doing repetitive volatile loads [which may still be cheap], this resolution always normal loads after initial construction).
使用私有静态类加载单例。在调用之前不会加载类,因此在类加载之前不会加载引用。通过实现进行的类加载是线程安全的,并且您也产生很少的开销(如果您正在执行重复的易失性加载 [这可能仍然很便宜],此解决方案在初始构造后始终正常加载)。
class MyClass {
public static Resource getInstance() {
return ResourceLoader.RESOURCE;
}
private static final class ResourceLoader {
private static final Resource RESOURCE = new Resource();
}
}
回答by masoud
I think, you should use syncronized
keyword before getInstance
definition.
我认为,您应该syncronized
在getInstance
定义之前使用关键字。
For better performance you can use Double-checked locking pattern:
为了获得更好的性能,您可以使用双重检查锁定模式:
回答by hthserhs
When applied to a field, the Java volatile guarantees that:
当应用于一个字段时,Java volatile 保证:
(In all versions of Java) There is a global ordering on the reads and writes to a volatile variable. This implies that every thread accessing a volatile field will read its current value before continuing, instead of (potentially) using a cached value. (However, there is no guarantee about the relative ordering of volatile reads and writes with regular reads and writes, meaning that it's generally not a useful threading construct.)
(In Java 5 or later) Volatile reads and writes establish a happens-before relationship, much like acquiring and releasing a mutex.
(在所有 Java 版本中)对 volatile 变量的读取和写入具有全局顺序。这意味着访问 volatile 字段的每个线程将在继续之前读取其当前值,而不是(可能)使用缓存值。(但是,无法保证 volatile 读取和写入与常规读取和写入的相对顺序,这意味着它通常不是有用的线程构造。)
(在 Java 5 或更高版本中)Volatile 读取和写入建立了一个发生在之前的关系,就像获取和释放互斥锁一样。
More info.
更多信息。
回答by Jason Sankey
You are correct, in this case the Resource may be constructed twice due to the race you describe. If you want to implement a singleton (without explicit locking) in Java 5+, use an enum singleton as described in answers to What is an efficient way to implement a singleton pattern in Java?.
您是对的,在这种情况下,由于您描述的种族,资源可能会被构造两次。如果您想在 Java 5+ 中实现单例(没有显式锁定),请使用枚举单例,如在 Java 中实现单例模式的有效方法是什么?.
回答by ssedano
volatile
keyword guarantees that read and write to that variable are atomic.
volatile
关键字保证对该变量的读取和写入是原子的。
According to the tutorial
根据教程
Reads and writes are atomic for all variables declared volatile
Using volatile variables reduces the risk of memory consistency errors, because any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable. This means that changes to a volatile variable are always visible to other threads. What's more, it also means that when a thread reads a volatile variable, it sees not just the latest change to the volatile, but also the side effects of the code that led up the change.
使用 volatile 变量可降低内存一致性错误的风险,因为对 volatile 变量的任何写入都会与对该同一变量的后续读取建立先发生关系。这意味着对 volatile 变量的更改始终对其他线程可见。更重要的是,这也意味着当线程读取 volatile 变量时,它不仅会看到 volatile 的最新更改,还会看到导致更改的代码的副作用。
回答by Bytekoder
first of all, having a Singleton this way, you are essentially creating a global object which is a bad practice. I reckon you use Enums instead.
首先,以这种方式拥有一个单例,您实际上是在创建一个全局对象,这是一种不好的做法。我认为您使用 Enums 代替。
回答by Nosyara
Here is my suggestion to add volatile & synchronized together.
这是我将 volatile 和 synchronized 一起添加的建议。
Note: we still have to do double check.
注意:我们仍然需要仔细检查。
public class MySingleton {
private static volatile MySingleton instance;
private MySingleton() {}
synchronized private static void newInstance() {
if(instance == null) {
instance = new MySingleton();
}
}
public static MySingleton get() {
if(instance == null) {
newInstance();
}
return instance;
}
}