Java 单例类方法的并发调用
我有一个单身班:Java 单例类方法的并发调用,java,multithreading,concurrency,singleton,singleton-methods,Java,Multithreading,Concurrency,Singleton,Singleton Methods,我有一个单身班: public class Singleton { private static Singleton istance = null; private Singleton() {} public synchronized static Singleton getSingleton() { if (istance == null) istance = new Singleton(); return i
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);
}
}
}
我注意到这两个线程同时运行,就好像两个工作函数同时被实例化一样
我希望最后一个线程代替上一个线程运行,而不是并发运行。在java中是否可以使第二个调用覆盖第一个调用的内存空间?正如@amit在注释中所述,您的
getSingleton()
方法应该是同步的。原因是多个线程可能同时请求一个实例,而第一个线程仍将初始化对象,并且在下一个线程检查时引用将为null。这将导致创建两个实例
public static synchronized Singleton getSingleton() {
if (istance == null)
istance = new Singleton();
return istance;
}
将方法标记为“已同步”
将导致它阻塞,并且一次只允许一个线程调用它。这将解决您的问题。在factory方法上使用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
// ...
}
}
或者,简单地说,使用私有的最终初始值设定项(实例化将在类加载时发生)
您的getSingleton()
方法正在尝试删除SINGLETON实例,但存在以下问题:
- 对变量的访问未同步
- 变量不是易变的
- 你没有使用
private static class Holder {
static Singleton instance = new Singleton();
}
public static Singleton getSingleton() { // Note: "synchronized" not needed
return Holder.instance;
}
这是线程安全的,因为java类加载器的约定是所有类在使用之前都完成了静态初始化。此外,类加载器在被引用之前不会加载类。如果两个线程同时调用getSingleton()
,那么Holder
类仍然只会加载一次,因此newsingleton()
只会执行一次
这仍然是惰性的,因为Holder
类仅从getSingleton()
方法引用,因此只有在第一次调用getSingleton()
时才会加载Holder
类
不需要同步,因为此代码依赖于类装入器的内部同步,这是防弹的
此代码模式是使用单例飞行的唯一方法。它是:
- 最快的(无同步)
- 最安全(取决于工业强度等级装载机安全性)
- 最干净的(最少的代码-双重检查锁定很难看,而且它的功能有很多行)
另一种类似的代码模式(同样安全和快速)是对单个实例使用
enum
,但我发现这很笨拙,目的也不太清楚。Java Concurrency in Practice中给出的资源持有者:是可用的最佳非阻塞单例模式。singleton是延迟初始化的(当第一次调用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{
}
}我想出了这段代码,它几乎满足了我的需要。 最初的问题是“是否可以不使用线程而直接使用语言操纵内存来执行以下操作?”如果答案是否定的,也许您可以帮助我改进以下内容:
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();
}
}
公共类主{
私有静态线程t;
公共静态void main(字符串[]args){
工作();
对于(int i=0;i您可以在共享资源周围使用锁。使用可重入的类。它可以防止多线程的争用条件。我不确定您的问题是什么,但是您的getSingleton()
方法应该是同步的工作()
不应是静态的,否则该示例不会使senseI尝试同步,但两个线程仍同时运行删除静态两个工作()函数按顺序运行,一次一个。显然,第二个调用在第一个调用之后排队。第二个调用如何才能代替第一个调用运行?@Lukysynchronized
不会阻止两个线程同时运行:它只会序列化它们对getSingleton()的访问
函数。换句话说,这意味着您可以确定,无论当前有多少线程正在执行,在任何时间点,最多只有一个线程将执行getSingleton()
。在这种情况下,我们不应该删除synchronized
吗?@jcm哦,是的-忘记删除它了(我复制粘贴了原始impl并对其进行了修改)。立即修复。Thx。@Bohemian这仍然是Singleton最快、最安全和最好的方法吗?同步对getSingleton()
的每个调用效率不高。只需对getSingleton()的前几个调用
应进行同步,以确保实例已正确初始化和发布。@carbolymer此问题已存在5年,且相当模糊。从未提及效率。如果只是懒洋洋地实例化一个单例,则顶部答案中提到的holder模式是绕过同步的正确方法我唯一的问题是,work()一次由一个线程调用,因为Sigleton类在内存中只创建了一个对象。对吗?不,您可以从多个线程并发调用work()
(或getSingleton
)。如果这样做,您显然需要确保work()
可以安全地并发调用。好吧,但本质上,作为singleton中的类,一次只能有一个线程拥有对象的锁。因此方法可以工作()一次由一个线程使用。对吗?Singleton意味着只有一个类实例存在,而不是说它不能由多个线程并发使用。因此,正如我在上面所写的,没有任何东西可以阻止并发调用work()
。它们是完全正交的概念。
public class SingletonFactory {
private static class SingletonHolder {
static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonFactory.SingletonHolder.instance;
}
static class Singleton{
}
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();
}
}