--------------------------- 题记
查了很多资料,然后汇总总结了一篇单例模式的文章。
--------------------------- 查询的相关博客资料
单例模式-内部类:
http://blog.sina.com.cn/s/blog_6d2890600101gb8x.html
http://blog.sina.com.cn/s/blog_765e27790101ab8x.html
http://blog.youkuaiyun.com/shutear/article/details/8038847
Synchronized:
http://zhangjunhd.blog.51cto.com/113473/70300
http://hi.baidu.com/jiangyangw3r/item/a3d53ed6bb72b21e20e2504c
单例模式:
http://www.cnblogs.com/coffee/archive/2011/12/05/inside-java-singleton.html
http://blog.sina.com.cn/s/blog_75247c770100yxpb.html
http://blog.youkuaiyun.com/wutianyin222/article/details/7911829
--------------------------- 单例模式
有些时候,允许自由创建某个类的实例没有意义,还可能造成系统性能下降(因为创建对象锁带来的系统开销问题)。
使用单例模式的三个条件
◇ 使用private修饰该类的构造方法。
◇ 提供一个public static的公共静态方法作为该类的访问点。
◇ 必须缓存已创建的对象,使用private static私有静态成员变量类缓存。
使用代理模式的两个优势
◇ 减少创建Java实例所带来的系统开销。
◇ 便于系统跟踪单个Java实例的生命周期、实例状态等。
单例一:预加载
优点:线程安全,线程同时访问无性能下降问题。
缺点:加载类就创建对象,如果长期不使用,就导致资源浪费。
package test;
public class Singleton {
//静态缓存点
private static Singleton singleton = new Singleton();
//私有构造方法
private Singleton() {}
//静态访问点
public static Singleton getInstance() {
return singleton;
}
}
单例二:延迟加载(同步方法锁)
优点:延迟加载,按需分配内存,线程安全(懒汉模式)。
缺点:多线程访问时,性能降低。
package test;
public class Singleton {
//静态缓存点
private static Singleton singleton;
//私有构造方法
private Singleton() {}
//静态访问点
public synchronized static Singleton getInstance() {//给方法上锁,不同线程不能同时进入该方法
if (singleton == null) singleton = new Singleton();
return singleton;
}
}
注:对static方法使用synchronized时,加锁的对象为SingletonDemo.class,这个是线程安全的。如果对于成员方法(没有static修饰),加锁的对象为SingletonDemo.this,如果其他线程该类的其他对象访问该方法,synchronized对它是无效的,因为是对象锁,锁的是本对象其他线程对于该方法的访问。
单例三:延迟加载(双重检查锁)
优点:延迟加载,按需分配内存,线程安全(懒汉模式)。
缺点:第一次线程同时访问时性能下降。
package test;
public class Singleton {
//静态缓存点
private static Singleton singleton;
//私有构造方法
private Singleton() {}
//静态访问点
public static Singleton getInstance() {
if (singleton == null)
synchronized (Singleton.class) {//给该类上锁,不同线程不能同时进入该类同步块
if (singleton == null) singleton = new Singleton();
}
return singleton;
}
}
注:如代码所示,此代码如果有成员变量需要对外访问,那么需要加入volatile关键字修饰对应的对象。若没有该关键字,则获取到的该成员变量不准确,该单例是个不健康的单例模式。若使用该关键字,则性能会降低,少了JVM的代码优化功能(具体看博客分析)。
单例四:延迟加载(内部类)
package test;
public class Singleton {
private static class SingletonHolder {
//静态缓存点
private static Singleton singleton = new Singleton();
}
//私有构造方法
private Singleton() {}
//静态访问点
public static Singleton getInstance() {
return SingletonHolder.singleton;
}
}
注:因为java机制规定,内部类SingletonHolder只有在getInstance方法第一次调用的时候才会被加载(实现了lazy),而且其加载过程是线程安全的(JVM实现线程安全)。内部类加载的时候实例化一次singleton。
单例模式举例
◇ Servlet工作流程图如下:
那么在整个过程中,首先是容器接受请求,再把所有请求发送给dispatch中转站,中转站解析请求之后,根据请求的信息来调用不同的servlet,在调用servlet的时候使用多线程来代替多进程,从而提高性能。
在这过程中,有两个地方是单例,第一个是Dispatch类是个单例模式,第二个是Servlet容器,是个容器类型的单例。
◇ Servlet中的单例:
● Dispatch:标准的单例模式。
● Servlet容器:使用Map<String, Object>缓存的单例模式。
package test;
public class Singleton {
//静态缓存点
private static Singleton singleton;
//私有构造方法
private Singleton() {}
//静态访问点(工厂)
public static Singleton factory(String className) {
if (singleton == null)
synchronized (Singleton.class) {
try {
if (singleton == null)
singleton = (Singleton) Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
return singleton;
}
}