单例模式其实在我们平常并不少见,比如任务管理器、回收站、项目中读取配置文件的类、Application都是典型的单例模式,网站的计数器、数据库连接池等的设计一般也是采取单例模式。
一.单例模式的特点
- 只有一个实例
- 自行实例化(必须创建自己的唯一实例)
- 向整个系统提供这个实例
通俗一点的说就是,保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
- 其优点就是因为只生成一个实例,减少了系统性能开销,当一个对象需要较多资源时,如读取配置,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存来解决。
- 单例模式可以在系统设置全局的访问点,优化功效资源访问。
二.单例模式的实现方式
主要方式:
单例模式主要是通过以下思路实现:
- 构造器私有化(避免外部new构造器)
- 提供私有的静态属性(存储对象的地址)
- 提供公共的静态方法(获取属性)
其中在提供私有的静态属性时就new实例,称为饿汉式(饿了就吃), 在提供的公共的静态方法中new实例,成为懒汉式(饿了才开始做)。
1.饿汉式单例:线程安全,调用效率高,不能延时加载
public class Singleton {
private static final Singleton instance = new Singleton();
//私有构造器,保证了Singleton的全局唯一性,限制产生多个对象
private Singleton() {
}
//静态工厂方法,对于此方法的调用,都会返回同一个对象引用
public static Singleton getInstance() {
return instance;
}
}
饿汉式:
- 提供私有的静态属性时就new实例,所以在类初始化时,会立即加载这个属性,提供了一个对象供外部使用,除非系统重启,这个对象不会改变,因此是不能延时加载,线程安全的。
- 也正因为是天然系统安全的,所以可以省略synchronized同步,因此调用效率高。
2.懒汉式单例:线程不安全,调用效率不高,可以延时加载
private static Singleton singleton = null;
private Singleton() {
}
public static Singleton getSingleton() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
懒汉式:
- 因为在提供的公共的静态方法中才new实例,所以在多线程环境下会产生多个singleton对象,是线程不安全的,因此不建议。
- 如果想解决线程安全可以考虑使用synchronized同步锁,即synchronized修饰getSingleton() 方法,也可以使用synchronized修饰代码块。
如:
private static Singleton singleton = null;
private Singleton() {
}
public static synchronized Singleton getSingleton() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
注:同步锁虽然解决了多个singleton对象问题,但每次调用getSingleton()方法都要同步,会导致允许效率低下。
其他方式:
1.双重检测锁式
private static Singleton singleton = null;
private Singleton() {
}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized(Singleton.class){
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
双重检测锁式:
- 这个模式将同步内容下放到if内部,提高了执行的效率,不必每次都进行同步,只有第一次才同步创建了以后就没有必要了。
- 但是由于JVM底层内部模型原因,偶尔会出问题,不建议使用。
2.静态内部类式(线程安全,调用效率高,可以延时加载)
private static class SingletonClassInstance{
private static final Singleton instance = new Singleton();
}
private Singleton() {
}
public static Singleton getSingleton() {
return SingletonClassInstance.instance ;
}
静态内部类式:
- 外部没有static属性,因此不会像饿汉式那样立即加载对象。
- 只有真正调用了getInstance(),才会加载静态内部类。加载类是是线程安全的。instance 是 private static final ,保证了内存中只有这样的一个实例。
- 结合了并发高校调用和延迟加载的优势。
3.枚举(线程安全,调用效率高,不能延时加载)
public enum Singleton {
//枚举元素,本身就是单例对象
INSTANCE;
//添加自己需要的操作
public void SingletonOpt(){}
}
枚举式:
- 从Java1.5发行版本开始,这种方法更加简洁,并且无偿提供了序列化机制,绝对防止多次实例化。
- 虽然这种这种方法还没有广泛使用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。
三.应用
说明:中国历史上的皇帝通常仅有一人。为了保障其唯一性,古人采用玉玺等来进行防伪标识。更简单的方法是限制皇帝的创建。比如,使用单例模式来保证皇帝的唯一性。
Emperor类:
public class Emperor
{
private static Emperor emperor=null;
private Emperor(){}
public static Emperor getEmperor(){
if (emperor==null){
emperor=new Emperor();
}
return emperor;
}
}
注意线程安全问题,可以自己思考以下改动(^U^)ノ~YO
最近才开始了解设计模式的,所以这也只是看了一些文章之后做的整合,自己的一些看法,如果有什么地方不对的,欢迎大家指出。
学习参考:
https://blog.csdn.net/jason0539/article/details/44956775
《effective Java中文版第二版第二章第三条》《Java编程300例》《尚云堂视频讲解》