单例模式作为常见的设计模式之一,在java的项目开发中会时常的用到。Java Singleton模式即保证在JVM运行时,一个类Class只有一个实例存在。
一、单例模式的作用
单例模式有什么好处呢?
最简单的一个例子就是网站计数器的设计了。当我们想要统计当前网站的在线人数时,一个显而易见的问题就是并发所带来的线程安全问题,当我们对这个计数器(网站人数)在同一时刻进行操作,再保存计数时就会造成数据的混乱,后者覆盖前者的结果。一种解决方案就是把这个计数器设置为唯一对象,所有人都必须共用同一份数据。
实现唯一对象最好的解决办法就是让类自己负责保存它的唯一实例,并且让这个类保证不会产生第二个实例,同时提供一个让外部对象访问该实例的方法。自己的事情自己办,而不是由别人代办,这非常符合面向对象的封装原则。
二、单例模式的创建
单例模式的三个特点:
- 单例类确保自己只有一个实例
- 单例类必须自己创建自己的实例
- 单例类必须为其他对象提供唯一的实例
单例模式的实现方法有许多种,下面介绍几种常用的实现方法
1. 懒汉式
只有在自身需要的时候才会行动,从来不知道及早做好准备。它在需要对象的时候,才判断是否已有对象,如果没有就立即创建一个对象,然后返回,如果已有对象就不再创建,立即返回。
/**
* @author zmrwego
* @descreption
*
* 懒汉式,线程不安全的
*
* @create 2018-10-17
**/
public class Singleton1 {
/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
private static Singleton1 instance = null;
//私有构造器,只允许自己实例化自己
private Singleton1(){}
//懒汉式 静态方法,创建实例
public static Singleton1 getInstance(){
if(instance == null){
instance = new Singleton1();
}
return instance;
}
}
该方法在多线程情况下有可能重复创建实例,以下是线程安全的懒汉模式
/**
* @author zmrwego
* @descreption 懒汉式变种,加同步锁
* @create 2018-10-17
**/
public class Singleton2 {
private static Singleton2 instance =null;
private Singleton2(){}
public static synchronized Singleton2 getInstance() {
if (instance == null){
instance = new Singleton2();
}
return instance;
}
}
这种模式的缺点是加锁造成了效率下降,并且在绝大部分情况下是不需要同步的。使用双重检验锁(DCL),只在第一次初始化的时候进行同步加锁
/**
* @author zmrwego
* @descreption、
*
*懒汉模式下的双重检验锁,使用volatile关键字
*防止jvm指令重排序的优化导致的
*
* @create 2018-10-17
**/
public class SycSingleton {
private static volatile SycSingleton instance = null;
private SycSingleton(){}
public static SycSingleton getInstance(){
if (instance == null){
synchronized (SycSingleton.class){
if (instance == null){
instance = new SycSingleton();
}
}
}
return instance;
}
}
2. 饿汉式
该方式在类加载的时候就被实例化了。
public class Singleton {
//声明为静态对象,在类加载时即创建好了实例
private static Singleton instance = new Singleton();
private Singleton(){}
//
private static Singleton getInstance(){
return instance;
}
}
3. 静态内部类
这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟饿汉不同的是(很细微的差别):饿汉方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显式通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。
public class Singleton {
// 静态内部类
private static class SingletonHoder {
private static Singleton instance = new Singleton();
}
private Singleton() {=o}
public static Singleton getInstance() {
return SingletonHoder.instance;
}
}
想象一下,如果实例化instance很消耗资源,我想让它延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式就显得很合理。
关于类加载情况下单例模式,如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类 装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。修复的办法是
网友评论