目录
1.介绍:
2.单例模式的结构
3. 单例模式的实现两种。
3.1.静态变量实现
3.2 饿汉式:静态代码块实现:
3.3 懒汉式 (同步方法)
3.4 懒汉式(双重检查锁)
3.5 静态内部类,懒汉式
3.6 枚举(饿汉式)
1.介绍:
单例模式(Singleton Pattern)是Java中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象。同时确保只有单个对象被创建。这个提供一一种访问其唯一的对象的方法,可以直接访问,不需要实例化类的对象。
2.单例模式的结构
~单例类。只能创建一个实例的类。
~访问类。使用单例类
3. 单例模式的实现两种。
饿汉式:类加载就会导致该单例对象被创建。
懒汉式:类加载不会导致该单例对象被创建,而是首次使用该对象时才会创建。
3.1.静态变量实现
饿汉式,静态变量方式
/**
* 饿汉式,静态成员变量
* @author CharlieLiang
*
*/
public class Singleton {
// 1.私有构造方法
private Singleton() {};
// 2.在本类中创建本类对象
private static Singleton instance=new Singleton();
// 3.提供一个公共访问方法,让外界获取该对象
public static Singleton getInstance() {
return instance;
}
}
public class Client {
public static void main(String[] args) {
Singleton s1=Singleton.getInstance();
Singleton s2=Singleton.getInstance();
System.out.println("s1的哈希值:"+s1.hashCode());
System.out.println("s2的哈希值:"+s2.hashCode());
System.out.println("s1的地址:"+s1);
System.out.println("s2的地址:"+s2);
System.out.println("s1和s2是否相等:"+(s1==s2));
}
}
运行结果:
说明:该方式在成员位置声明Singleton类型的静态变量,并创建Singleton类的对象instance。instance对象是随着类的加载而创建的。如果该对象足够大,而一直没有使用就会造成内存的浪费。
3.2 饿汉式:静态代码块实现:
/**
* 饿汉式,静态代码块
* @author CharlieLiang
*
*/
public class Singleton {
// 构造方法私有化
private Singleton() {};
// 声明Singleton的变量
private static Singleton instance;
// 在静态代码块中进行赋值
static {
instance=new Singleton();
}
public static Singleton getInstance() {
return instance;
}
}
public class Client {
public static void main(String[] args) {
Singleton s1=Singleton.getInstance();
Singleton s2=Singleton.getInstance();
System.out.println("s1的哈希值:"+s1.hashCode());
System.out.println("s2的哈希值:"+s2.hashCode());
System.out.println("s1的地址:"+s1);
System.out.println("s2的地址:"+s2);
System.out.println("s1和s2是否相等:"+(s1==s2));
}
}
运行结果
说明:该方式在成员位置声明Singleton类型的静态变量,而对象的创建是在静态代码块中,也是对着类的加载而创建。所以和饿汉式的方式1基本上一样,当然该方式也存在内存浪费问题。
3.3 懒汉式 (同步方法)
/**
* 懒汉式
* @author CharlieLiang
*
*/
public class Singleton {
private Singleton() {};
// 只声明一个,没有赋值
private static Singleton instance;
// 同步方法,线程安全
public static synchronized Singleton getInstance() {
// 判断instance是否为null,如果是null,说明还没有创建Singleton类的对象
// 如果没有,创建一个并返回,如果有,直接返回
if(instance==null) {
// 线程1等待,线程2获取到cpu的执行权,也会进入该判断里面
instance=new Singleton();
}
return instance;
}
}
public class Client {
public static void main(String[] args) {
Singleton s1=Singleton.getInstance();
Singleton s2=Singleton.getInstance();
System.out.println("s1的哈希值:"+s1.hashCode());
System.out.println("s2的哈希值:"+s2.hashCode());
System.out.println("s1的地址:"+s1);
System.out.println("s2的地址:"+s2);
System.out.println("s1和s2是否相等:"+(s1==s2));
}
}
运行结果:
说明:
该方式也实现了懒加载效果,同时又解决了线程安全问题。但是在getInstance()方法上添加了synchronized关键字,导致该方法的执行效率特别低。
3.4 懒汉式(双重检查锁)
/**
* 懒汉式,双重检查锁
* @author CharlieLiang
*
*/
public class Singleton {
private Singleton() {};
// 只声明一个,没有赋值
private static volatile Singleton instance;
public static Singleton getInstance() {
// 第一次判断,如果instance不是null,不需要强占资源,直接返回对象
if(instance==null) {
synchronized (Singleton.class) {
// 第二次判断
if(instance==null) {
instance =new Singleton();
}
}
}
return instance;
}
}
public class Client {
public static void main(String[] args) {
Singleton s1=Singleton.getInstance();
Singleton s2=Singleton.getInstance();
System.out.println("s1的哈希值:"+s1.hashCode());
System.out.println("s2的哈希值:"+s2.hashCode());
System.out.println("s1的地址:"+s1);
System.out.println("s2的地址:"+s2);
System.out.println("s1和s2是否相等:"+(s1==s2));
}
}
运行结果:
说明:
双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,上面的双重检测锁模式看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。
要解决双重检查锁模式带来空指针异常的问题,只需要使用 volatile
关键字, volatile
关键字可以保证可见性和有序性。
3.5 静态内部类,懒汉式
/**
* 懒汉式,静态内部类
* @author CharlieLiang
*
*/
public class Singleton {
private Singleton() {};
// 静态内部类
private static class SingletonHolder {
// 在内部类声明并初始化外部类对象
private static final Singleton INSTANCE=new Singleton();
}
// 提供公共访问方式
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
public class Client {
public static void main(String[] args) {
Singleton s1=Singleton.getInstance();
Singleton s2=Singleton.getInstance();
System.out.println("s1的哈希值:"+s1.hashCode());
System.out.println("s2的哈希值:"+s2.hashCode());
System.out.println("s1的地址:"+s1);
System.out.println("s2的地址:"+s2);
System.out.println("s1和s2是否相等:"+(s1==s2));
}
}
运行结果:
说明:
第一次加载Singleton类时不会去初始化INSTANCE,只有第一次调用getInstance,虚拟机加载SingletonHolder
并初始化INSTANCE,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。
3.6 枚举(饿汉式)
说明:
枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。
/**
* 饿汉式,枚举类型
* @author CharlieLiang
*
*/
public enum Singleton {
INSTANCE;
}
public class Client {
public static void main(String[] args) {
Singleton s1=Singleton.INSTANCE;
Singleton s2=Singleton.INSTANCE;
System.out.println("s1的哈希值"+s1.hashCode());
System.out.println("s2的哈希值"+s2.hashCode());
System.out.println("s1和s2是否相等:"+(s1==s2));
}
}
运行结果: