设计模式—单例模式
所谓单例模式,就是采取一定的方法来保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。例如:Hibernate的SessionFactory,java.lang.Runtime(饿汉式)
1.饿汉式(静态常量)
2.饿汉式(静态代码块)
3.懒汉式(线程不安全)----不可用
4.懒汉式(线程安全,同步方法)
5.懒汉式(线程安全,同步代码块)----不可用
6.双重检查
7.静态内部类
8.枚举
使用场景:需要频繁地创建和销毁的对象、创建对象时耗时过多或者耗费资源过多(重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)
注意:想要实例化一个单例类时,必须使用相应的获取对象的方法,而不要new
1.饿汉式(静态常量)
1)构造器私有化----->防止通过new来创建多个对象实例
2)类的内部创建对象----->因为构造器已经私有了,所以已经无法在类的外部创建任何该类的对象了
3)对外暴露一个公共的获取对象实例的方法---->为在外部获取该唯一的实例提供方法
//饿汉式(静态常量)
package singleton.type1;
public class SingletonTest01 {
public static void main(String[] args) {
// 测试
Singleton instance = Singleton.getInstance();
// 再次获取该类的实例
Singleton instance2 = Singleton.getInstance();
// 输出true,说明两次获得的实例其实是同一个
System.out.println(instance == instance2);
// 两个实例的hashcode值相等,说明是同一个实例
System.out.println(instance.hashCode());
System.out.println(instance2.hashCode());
}
}
class Singleton {
// 1.构造器私有化
private Singleton() {}
// 2.在本类的内部创建一个对象实例
//为什么static:因为在外部时不能创建对象的(构造器私有),所以应该为类变量
private final static Singleton instance = new Singleton();
// 3.对外提供一个公有的静态方法返回对象的实例
//为什么static:因为在外部我们需要直接使用类来调用该公共方法来获取唯一的实例
public static Singleton getInstance() {
return instance;
}
}
优点:写法简单,在类加载的时候就完成了实例化,避免了线程同步的问题
缺点:在类加载的时候就进行实例化,没有达到懒加载的效果,如果从始至终没有使用到该实例,会造成内存的浪费
结论:可用,但是可能会造成内存浪费
2.饿汉式(静态代码块)
//饿汉式(静态代码块)
class Singleton{
//1.构造器私有
private Singleton(){}
//2.在本类的内部声明该对象的引用
private static Singleton instance;
//3.在静态代码块中初始化单例对象,静态代码块会在类加载的时候执行
static{
instance = new Singleton();
}
//4.对外提供获取该唯一的对象实例的方法
public static Singleton getInstance(){
return instance;
}
}
类似于静态常量的饿汉式,只不过是将类的实例化过程放在静态代码块中,也就是在类加载的时候就执行静态代码块中的代码,初始化类的实例,优缺点同饿汉式(静态常量)
结论:可用,但是可能会浪费内存(未使用到该实例时)
3.懒汉式(线程不安全)
class Singleton {
private static Singleton instance;
// 私有化构造器
private Singleton() {
}
// 提供一个静态的公有方法,当使用到该方法时,才会创建instance(即懒汉式)
public static Singleton getInstance() {
//判断是否已经实例化,如果没有,则进行实例化
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优缺点:可以达到懒加载(在需要使用是采取加载)的效果,但是只能在单线程下使用;
线程不安全:在多线程时,如果一个线程进入了if(singleton==null)这个判断内,还未来得及往下运行,此时另一个线程也通过了该if判断,这时便会创建多个实例,所以在多线程环境下不可以使用这种方式
结论:实际开发中不要使用该方式
4.懒汉式(线程安全,同步方法)
class Singleton {
private static Singleton instance;
// 私有化构造器
private Singleton() {
}
// 提供一个静态的公有方法,当使用到该方法时,才会创建instance(即懒汉式)
// 加上synchronized,解决线程不安全的问题
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优缺点:解决了线程不安全的问题;效率低,每个线程在获取类的实例时,执行getInstance()方法都要进行同步,而实际上这个方法只执行一次实例化代码就足够了,后面的想获得该类的实例,直接返回instance就可以。
结论:实际开发中不推荐使用
5.懒汉式(线程安全,同步代码块)
class Singleton {
private static Singleton instance;
private Singleton() { }
// 提供一个静态的公有方法,当使用到该方法时,才会创建instance(即懒汉式)
public static Singleton getInstance() {
if (instance == null) {
// 加上synchronized,同步代码块
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
优缺点:这种同步并不能起到线程同步的作用,多线程情况时,假如一个线程进入了if判断语句块,还未来得及向下执行,另一个进程也进入了该if判断的语句块,便会产生多个实例(即:线程1进入if语句块,还未向下执行时,线程2进入if语句块;线程1继续向下执行同步代码块,此时线程2等待线程1执行完毕;线程1执行完毕,创建了一个实例,退出同步代码块;线程2进入同步代码块,进行实例化)
结论:实际开发中不能使用该方式,不是真正的单例
6.双重检查
class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
// 提供一个静态的公有方法,当使用到该方法时,才会创建instance(即懒汉式)
// 加入双重检查代码,解决线程安全的问题,同时保证了效率
public static Singleton getInstance() {
if (instance == null) {
// 加上synchronized,同步代码块
synchronized (Singleton.class) {
// 再次判断instance是否尚未实例化
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
优缺点:
实例化代码只会执行一次,后面再访问时,如果if(instance == null),直接返回实例化对象,也避免了反复进行方法同步
实现了线程同步、延迟加载,同时保证了较高的效率
结论:实际开发时推荐使用该种单例模式
7.静态内部类
静态内部类的特点:外部类的加载不会导致静态内部类的加载;加载静态内部类时线程是安全的
class Singleton {
//构造器私有
private Singleton() {}
//写一个静态内部类,该类中有一个静态的属性
private static class SingletonInstance{
private static final Singleton INSTANCE = new Singleton();
}
//提供一个静态的共有方法,直接返回SingletonInstance的静态成员变量
public static Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
利用了类装载的机制来保证初始化实例时只有一个线程
静态内部类方式在Singleton类被装载的时候不会立即被实例化,而是在需要实例化时,调用getInstance方法才会装载内部的SingletonInstance类,从而完成Singleton的实例化
类的静态属性只会在第一次加载类的时候初始化,,所以这里JVM帮助我们保证了线程的安全,在内部类加载并初始化静态成员时,别的线程是无法进入的
优点:避免了线程不安全,利用静态内部类的特点实现了延迟加载,效高
结论:推荐使用
8.枚举
package singleton.type8;
public class SingletonTest08 {
public static void main(String[] args) {
// 利用枚举获取实例
Singleton instance = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
// 输出true,且hashcode值相同
System.out.println(instance == instance2);
System.out.println(instance.hashCode());
System.out.println(instance2.hashCode());
//执行枚举类内的方法
instance.sayOK();
}
}
//定义枚举类
enum Singleton {
INSTANCE; //定义一个枚举变量以保证时单例
public void sayOK() {
System.out.println("ok");
}
}
优缺点:可以避免多线程同步的问题,而且还能防止反序列化重新创建新的对象
结论:推荐使用