Java-java单例模式(懒加载)最好的实现方式?
请问大家都知道java的懒加载单例模式有哪些实现方式?我提出两种,一个是内部类实现方式:
public class Manager {
private Manager() {
}
public static Manager getInstance() {
return ManagerHolder.instance;
}
private static class ManagerHolder {
private static final Manager instance = new Manager();
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
饿汉的获取实例是静态的,在自己被加载时即被实例化,单从资源利用效率角度来讲,这个比懒汉式单例类稍差些。从速度和反应时间角度来讲,则比懒汉式单例类稍好些。然而,懒汉式单例类在实例化时,必须处理好在多个线程同时首次引用此类时的访问限制问题,特别是当单例类作为资源控制器,在实例化时必然涉及资源初始化,而资源初始化很有可能耗费时间。这意味着出现
多线程同时首次引用此类的机率变得较大。
第一种好,因为第二种用到了同步,对多线程不利。
此外jdk1.5之后还有更简单的用violate和double locking来实现
/**
* @author Administrator
* 没错的话应该是这样的
*/
public class Manager
{
private static Manager manager;
private Manager()
{}
public static Manager getInstance()
{
return (manager==null)?new Manager():manager;
}
}
另外还有一个是:登记式,将实例存放在Map中,未登记的先登记,已经登记的直接取用。
(例子是:《Java与模式》中的,建议去看看这本书,里面讲的很清楚)
[上面两个:
饿汉模式:在自己被加载时就将自己实例化
实例是static final类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性
懒汉模式:主要是将实例化的过程抽象出来 放在一个sychronized方法中执行。
在类加载时不创建单例实例。只在请求实例的时候才创建,并且不重复创建该类的实例。只不过在高并发的情况下,饿汉比懒汉速度快些。(个人认为2个方式都比较好)]
登记式:例子
public class RegSingleton {
/**
* 登记薄,用来存放所有登记的实例
*/
private static Map<String, RegSingleton> m_registry = new HashMap();
//在类加载的时候添加一个实例到登记薄
static {
RegSingleton x = new RegSingleton();
m_registry.put(x.getClass().getName(), x);
}
/**
* 受保护的默认构造方法
*/
protected RegSingleton() {
}
/**
* 静态工厂方法,返回指定登记对象的唯一实例;
* 对于已登记的直接取出返回,对于还未登记的,先登记,然后取出返回
* @param name
* @return RegSingleton
*/
public static RegSingleton getInstance(String name) {
if (name == null) {
name = "RegSingleton";
}
if (m_registry.get(name) == null) {
try {
m_registry.put(name, (RegSingleton) Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return m_registry.get(name);
}
/**
* 一个示意性的商业方法
* @return String
*/
public String about() {
return "Hello,I am RegSingleton!";
}
}
单例模式
之前看到过一个对单例模式各种写法的分析!受益匪浅!推荐!
引用原文中的一段话:
单例模式的写法有很多,在开发设计工作时,应当既要考虑到需求可能出现的扩展与变化,也应当避免“幻影需求”导致无谓的提升设计、实现复杂度,最终反而带来工期、性能和稳定性的损失。设计不足与设计过度都是危害,所以说没有最好的单例模式,只有最合适的单例模式。
考虑到类的继承所造成的隐患,可以将singleton模式的类用final关键字修饰。
即以如下的方式创建
public final class Singleton{
//...
}
非线程安全的:
public class Singleton {
private static Singleton instance;
public static Singleton getInstance() {
if( instance == null ) {
instance = new Singleton();
}
return instance;
}
private Singleton() {}
public static void main( String [] args ) {
Singleton instance = Singleton.getInstance();
// ... manipulate instance
}
}
线程安全的:
public class Singleton {
private static Singleton instance;
public synchronized static Singleton getInstance() {
if( instance == null ) {
instance = new Singleton();
}
return instance;
}
private Singleton() {}
public static void main( String [] args ) {
Singleton instance = Singleton.getInstance();
// ...
}
}
java 5之后用enum是最好的方法--effective java
以下是用楼主的例子改的enum的方法:
public enum Manager {
INSTANCE
private Manager() {
//do something if needed
}
public void someMethod(){
//do something
}
public static void someStaticMethod(){
//do something
}
}
如果要invoke的话可以这样:
Manager.INSTANCE.someMethod();
Manager.someStaticMethod();
这样做的好处就是不用写readResolve方法来防止多次反序列化后产生多个instance,还有就是防止利用Reflection + setAccessable(true)的单例攻击。
当然这个枚举类在创造实例的时候是threadsafe且是lazy initialized的,因为enum里的枚举值相当于public static final的,Enum实例(INSTANCE)的初始化是在Enum的class被加载时才会发生,而Java只有在Enum类第一次被引用时才会去加载
楼主提出的第一种方法,使用内部类的方式是最佳的方式,也是effective in java推荐的方式。
private static class ManagerHolder { private static final Manager instance = new Manager(); }
虚拟机类加载保证其只被初始化一次。无锁操作。
楼主提出的第二种方法如果getInstance不加锁,则不能保证初始化一次。