我有一个单例课程:
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); } } }
并且多个线程正在调用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(); } }
我注意到两个线程正在同时运行,就好像两个工作函数是同时实例化的一样。
我希望最后一个线程代替上一个线程运行,而不是同时运行。在Java中是否可以使第二个调用覆盖第一个调用的内存空间?
您的getSingleton()方法正在尝试延迟初始化 SINGLETON实例,但存在以下问题:
getSingleton()
synchronized
volatile
因此竞争条件AMY会导致创建两个实例。
最好和最简单的方法是安全地懒惰地初始化一个单例 而不 同步,如下所示:
private static class Holder { static Singleton instance = new Singleton(); } public static Singleton getSingleton() { // Note: "synchronized" not needed return Holder.instance; }
这是线程安全的,因为java类加载器的约定是所有类在使用之前都必须完成其静态初始化。同样,类加载器在被引用之前不会加载类。如果getSingleton()同时调用两个线程,则Holder该类仍将仅加载一次,因此new Singleton()将仅执行一次。
Holder
new Singleton()
这仍然是懒惰的,因为Holder类是 只 从引用getSingleton()的方法,所以Holder在第一次呼叫时类只会被加载 getSingleton()而成。
不需要同步,因为此代码依赖于类加载器的内部同步,这是防弹的。
此代码模式是单例飞行的唯一方法。它是:
另一种类似的代码模式(既安全又快速)是在enum单个实例中使用,但是我发现这样做很笨拙,目的并不明确。
enum