什么是单例设计模式?
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
单例模式的优点
- 由于在系统内存中只存在一个对象,因此可以节约系统资源,当需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。
- 避免对共享资源的多重占用。
使用场景
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如:
- 需要频繁实例化然后销毁的对象。
- 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
- 有状态的工具类对象。
- 频繁访问数据库或文件的对象。
Spring中的Bean的创建、数据库连接池的设计、多线程的线程池的设计一般都是采用单例模式。
单例的具体实现
- 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
- 在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型。
- 定义一个静态方法返回这个唯一对象。
一、饿汉式
饿汉式是立即加载的方式,使用类的时候已经将对象创建完毕,而且不管这个对象在以后会不会使用到,都会在项目启动的时候将对象实例化。
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
优点:实现起来简单,没有多线程同步问题。
缺点:当HungrySingleton该类被加载的时候,会初始化static的instance,静态变量被创建并分配内存空间,从这以后,这个static的instance对象便一直占着这段内存(即便你还没有用到这个实例),当类被卸载时,静态变量被摧毁,并释放所占有的内存,因此在某些特定条件下会耗费内存。
二、懒汉式
懒汉式不是立即加载的方式,它使用的方式是延迟加载,延迟加载就是调用get()方法时实例才被创建(先不急着实例化出对象,等要用的时候才给你创建出来。不着急,故又称为“懒汉模式”),常见的实现方法就是在get方法中进行new实例化。
懒汉式包含两种,一种是在方法上加synchronized字段确保线程安全,这种方式每次都会因为抢锁而等待,性能较差;一种是双重校验锁,如果发现对象已经实例化,则直接返回该对象,否则加锁(防止同一时间有多个请求判断,造成多个对象创建),创建对象。
public class LazySingleton {
private static volatile LazySingleton instance = null;
/**
* 避免类在外部被实例化
*/
private LazySingleton() {
}
/**
* 每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。
*
* @return
*/
public static synchronized LazySingleton getInstance() {
//getInstance 方法前加同步
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
/**
* 认为双重校验锁性能比上面的好一些,不需要每次都同步
*
* @return
*/
public static LazySingleton getInstance2() {
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
优点:使用synchronized,在多线程情形下,保证了“懒汉模式”的线程安全。
缺点:众所周知在多线程情形下,synchronized方法通常效率低,显然这不是最佳的实现方案。
三、静态内部类
这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在StaticInSingleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonHolder类,从而完成Singleton的实例化。
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
public class StaticInSingleton {
private StaticInSingleton() {
}
private static class SingletonHolder {
private static StaticInSingleton instance = new StaticInSingleton();
}
public static StaticInSingleton getInstance() {
return SingletonHolder.instance;
}
}
优点:延迟加载,调用SingletonHolder类时才初始化StaticInSingleton对象,线程安全,效率高。
四、枚举类
public enum SingletonEnum {
instance;
private SingletonEnum() {
}
public void method() {
}
public static void main(String[] args) {
// 访问方式
SingletonEnum.instance.method();
}
}
访问很简单在这里SingletonEnum.instance这里的instance即为SingletonEnum类型的引用,所以得到它就可以调用枚举中的方法了。借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。可能是因为枚举在JDK1.5中才添加,所以在实际项目开发中,很少见人这么写过,这种方式也是最好的一种方式,如果在开发中JDK满足要求的情况下建议使用这种方式。
优点:推荐使用
网友评论