在Java中实现单例设计模式的有效方法是什么?


当前回答

Stu Thompson发布的解决方案在Java 5.0及更高版本中有效。但我宁愿不使用它,因为我认为它容易出错。

人们很容易忘记不稳定的说法,也很难理解为什么有必要这样做。如果没有易失性,由于双重检查锁定反模式,该代码将不再是线程安全的。有关这一点的更多信息,请参阅《Java并发实践》第16.2.4段。简而言之:此模式(在Java5.0之前或没有volatile语句)可能返回对Bar对象的引用,该对象(仍然)处于错误状态。

这种模式是为性能优化而发明的。但这真的不再是一个真正的问题了。下面的惰性初始化代码很快,更重要的是,更容易阅读。

class Bar {
    private static class BarHolder {
        public static Bar bar = new Bar();
    }

    public static Bar getBar() {
        return BarHolder.bar;
    }
}

其他回答

使用枚举:

public enum Foo {
    INSTANCE;
}

乔舒亚·布洛赫(Joshua Bloch)在Google I/O 2008的“有效Java重载”演讲中解释了这种方法:视频链接。另请参见其演示文稿的幻灯片30-32(effecte_java_reloaded.pdf):

实现可串行化Singleton的正确方法公共枚举Elvis{实例;private final String[]收藏夹歌曲={“猎犬”,“心碎酒店”};public void printFavorites(){System.out.println(Arrays.toString(收藏夹歌曲));}}

编辑:“有效Java”的在线部分说:

“这种方法在功能上等同于公共字段方法,只是它更加简洁,免费提供了序列化机制,并且即使在复杂的序列化或反射攻击的情况下也能提供针对多个实例化的铁腕保证。虽然这种方法尚未被广泛采用,但单个元素枚举类型是实现一个公共字段的最佳方式。”英格顿。"

我见过的最好的单例模式使用Supplier接口。

它是通用的,可重复使用它支持延迟初始化它只有在初始化之前才同步,然后将阻塞供应商替换为非阻塞供应商。

见下文:

public class Singleton<T> implements Supplier<T> {

    private boolean initialized;
    private Supplier<T> singletonSupplier;

    public Singleton(T singletonValue) {
        this.singletonSupplier = () -> singletonValue;
    }

    public Singleton(Supplier<T> supplier) {
        this.singletonSupplier = () -> {
            // The initial supplier is temporary; it will be replaced after initialization
            synchronized (supplier) {
                if (!initialized) {
                    T singletonValue = supplier.get();
                    // Now that the singleton value has been initialized,
                    // replace the blocking supplier with a non-blocking supplier
                    singletonSupplier = () -> singletonValue;
                    initialized = true;
                }
                return singletonSupplier.get();
            }
        };
    }

    @Override
    public T get() {
        return singletonSupplier.get();
    }
}

这是如何实现一个简单的单例:

public class Singleton {
    // It must be static and final to prevent later modification
    private static final Singleton INSTANCE = new Singleton();
    /** The constructor must be private to prevent external instantiation */
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

这是如何正确地延迟创建单例:

public class Singleton {
    // The constructor must be private to prevent external instantiation
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    /**
     * The static inner class responsible for creating your instance only on demand,
     * because the static fields of a class are only initialized when the class
     * is explicitly called and a class initialization is synchronized such that only
     * one thread can perform it, this rule is also applicable to inner static class
     * So here INSTANCE will be created only when SingletonHolder.INSTANCE
     * will be called
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

在写它之前,真的要考虑一下为什么你需要一个单例。关于使用它们有一个准宗教的争论,如果你在Java中搜索单例,你很容易就会发现。

就我个人而言,出于许多原因,我尽量避免单身汉,其中大部分可以通过谷歌搜索单身汉找到。我觉得单身汉经常被虐待,因为他们很容易被每个人理解。它们被用作将“全局”数据引入OO设计的机制,因为它很容易绕过对象生命周期管理(或者真正思考如何从B内部实现a)。看看像控制反转(IoC)或依赖注入(DI)这样的事情,可以找到一个不错的中间地带。

如果你真的需要一个,那么Wikipedia有一个很好的例子来说明单例的正确实现。

我仍然认为在Java1.5之后,enum是可用的最佳单例实现,因为它还确保了即使在多线程环境中,也只创建一个实例。

public enum Singleton {
    INSTANCE;
}

你完蛋了!