java 单例类方法的并发调用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12878012/
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
concurrent calls of singleton class methods
提问by Luky
I have a singleton class:
我有一个单例类:
public class Singleton {
private static Singleton istance = null;
private Singleton() {}
public synchronized static Singleton getSingleton() {
if (istance == null)
istance = new Singleton();
return istance;
}
public void work(){
for(int i=0; i<10000; i++){
Log.d("-----------", ""+i);
}
}
}
And multiple Threads are calling the work() function:
并且多个线程正在调用 work() 函数:
public class Main {
public static void main(String[] args) {
new Thread (new Runnable(){
public void run(){
Singleton s = Singleton.getSingleton();
s.work();}
}).start();
System.out.println("main thread");
new Thread(new Runnable() {
public void run() {
Singleton s = Singleton.getSingleton();
s.work();
}
}).start();
}
}
I noticed the two Threads are running concurrently, as if two work functions were instantiated at the same time.
我注意到两个线程同时运行,就好像同时实例化了两个工作函数。
I want the last thread to be run in place of the previous thread, rather then concurrently. Is it possible in java to make the second call override the memory space of the first call?
我希望最后一个线程代替前一个线程运行,而不是并发运行。java中是否可以让第二次调用覆盖第一次调用的内存空间?
回答by Bohemian
Your getSingleton()
method is attempting to lazily initializingthe SINGLETON instance, but it has the following problems:
您的getSingleton()
方法正在尝试延迟初始化SINGLETON 实例,但存在以下问题:
- Access to the variable is not
synchronized
- The variable is not
volatile
- You are not using double checked locking
- 访问变量不是
synchronized
- 变量不是
volatile
- 您没有使用双重检查锁定
so a race condition AMY cause two instances to be created.
所以竞争条件 AMY 会导致创建两个实例。
The best and simplest was to safely lazily initialize a singleton withoutsynchronization is as follows:
最好和最简单的是安全地延迟初始化一个没有同步的单例如下:
private static class Holder {
static Singleton instance = new Singleton();
}
public static Singleton getSingleton() { // Note: "synchronized" not needed
return Holder.instance;
}
This is thread safe because the contract of the java class loader is that all classes have their static initialization complete before they may be used. Also, the class loader does not load a class until it is referenced. If two thread call getSingleton()
simultaneously, the Holder
class will still only get loaded once, and thus new Singleton()
will only be executed once.
这是线程安全的,因为 java 类加载器的约定是所有类在使用之前都完成了它们的静态初始化。此外,类加载器在引用之前不会加载类。如果两个线程getSingleton()
同时调用,Holder
该类仍然只会被加载一次,因此new Singleton()
只会被执行一次。
This is still lazy because the Holder
class is onlyreferenced from getSingleton()
method, so the Holder
class will only be loaded when the first call to getSingleton()
is made.
这仍然是惰性的,因为Holder
该类仅从getSingleton()
方法中引用,因此Holder
该类只会在第一次调用时加载 getSingleton()
。
Synchronization is not needed because this code relies on the class loader's internal synchronization, which is bullet proof.
不需要同步,因为此代码依赖于类加载器的内部同步,这是防弹的。
This code pattern is the only way to fly with singletons. It is:
这种代码模式是使用单例飞行的唯一方法。它是:
- The fastest (no synchronization)
- The safest (relies on industrial strength class loader safety)
- The cleanest (least code - double checked locking is ugly and a lot of lines for what it does)
- 最快(无同步)
- 最安全(依靠工业强度级装载机安全)
- 最干净的(最少的代码 - 双重检查锁定是丑陋的,而且它的作用有很多行)
The other similar code pattern (equally safe and fast) is to use an enum
with a single instance, but I find this to be clumsy and the intention is less clear.
另一个类似的代码模式(同样安全和快速)是将 anenum
与单个实例一起使用,但我发现这很笨拙,而且意图不太明确。
回答by Jim Mitchener
As @amit stated in a comment your getSingleton()
method should be synchronized
. The reason for this is that it is possible for multiple threads to ask for an instance at the same time and the first thread will still be initializing the object and the reference will be null when the next thread checks. This will result in two instances being created.
正如@amit 在评论中所述,您的getSingleton()
方法应该是synchronized
. 这样做的原因是多个线程可能同时请求一个实例,第一个线程仍然会初始化对象,并且在下一个线程检查时引用将为空。这将导致创建两个实例。
public static synchronized Singleton getSingleton() {
if (istance == null)
istance = new Singleton();
return istance;
}
Marking your method as synchronized
will cause it to block and only allow one thread at a time to call it. This should solve your problem.
synchronized
将您的方法标记为将导致它阻塞并且一次只允许一个线程调用它。这应该可以解决您的问题。
回答by CAFxX
Either use synchronized
on the factory method
要么synchronized
在工厂方法上使用
public class Singleton {
private static Singleton istance = null;
private final Singleton() {} // avoid overrides
public static synchronized Singleton getSingleton() {
if (istance == null)
istance = new Singleton();
return istance;
}
public void work() { // not static, otherwise there's no need for the singleton
// ...
}
}
or, simply, use a private final initializer (instantiation will happen at class-load time)
或者,简单地,使用私有最终初始化程序(实例化将在类加载时发生)
public class Singleton {
private static final Singleton istance = new Singleton(); // class-load initialization
private final Singleton() {}
public static Singleton getSingleton() { // no need for synchronized
return istance;
}
public void work() {
// ...
}
}
回答by clinton
Resource holder given in Java Concurrency In Practice:http://www.javaconcurrencyinpractice.com/is the best non-blocking singleton pattern available. The singleton is lazily initialized (both SingletonHolder and Singleton class is loaded at Run-Time when the getInstance() method is called the first time) and the access-or method is non-blocking.
Java Concurrency In Practice 中给出的资源持有者:http: //www.javaconcurrencyinpractice.com/是可用的最佳非阻塞单例模式。单例被延迟初始化(当第一次调用 getInstance() 方法时,SingletonHolder 和 Singleton 类都在运行时加载)并且访问或方法是非阻塞的。
public class SingletonFactory {
private static class SingletonHolder {
static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonFactory.SingletonHolder.instance;
}
static class Singleton{
}
}
}
回答by Luky
I came up with this code that is doing pretty much what I needed. The orignal question was "Is possible to do the following without using threads? But rather by directly manipulating the memory with the language?" If the answer is no, maybe you can help me improve the following:
我想出了这段代码,它几乎可以满足我的需求。最初的问题是“是否可以在不使用线程的情况下执行以下操作?而是通过使用语言直接操作内存?” 如果答案是否定的,也许您可以帮助我改进以下内容:
public class Main {
private static Thread t;
public static void main(String[] args) {
work();
for (int i =0;i<100; i++);
System.out.println("oooooooooooooooooooooooooooooooooooooooooo");
for (int i =0;i<100; i++);
work();
for (int i =0;i<500; i++);
System.out.println("oooooooooooooooooooooooooooooooooooooooooo");
}
public static void work(){
if (t != null) t.interrupt();
t= new Thread (new Runnable(){
public void run(){
// Pause for 4 seconds
try {
Thread.sleep(600);
} catch (InterruptedException e) {
// We've been interrupted: no more messages.
return;
}
for(int i=0; i<10000; i++){
System.out.println(i);
}
}
});
t.start();
}
}
This code is useful to "debounce" multiple calls to a listener, firing in a burst on user inputs. It has the disadvantages it uses a sleep function. The sleep time should be high enough to prevent events in a burst to start execution of the time consuming task (only the last event should). Unfortunately there is no guarantee that this can always happen even for a large sleep time.
此代码可用于“消除”对侦听器的多个调用,在用户输入时触发。它具有使用睡眠功能的缺点。睡眠时间应该足够长,以防止突发事件开始执行耗时的任务(只有最后一个事件应该)。不幸的是,即使在很长的睡眠时间内,也不能保证这总是会发生。
回答by Jainam Shah
You can use Locks around the shared resources. Use the Reentrant
class. It prevents race conditions for multiple threads.
您可以在共享资源周围使用锁。使用Reentrant
类。它可以防止多个线程的竞争条件。