一、概念
确保只生成一个实例对象的模式叫做单例模式
二、实现方式
1.静态变量初始化的方式(饿汉模式)常用
public class Singleton {
//声明为private只可以内部使用;
//声明为static在类初次加载时调用构造函数初始化,确保仅初始化一次
private static Singleton ourInstance = new Singleton();
//声明为private的构造函数只可内部调用
private Singleton() {
System.out.println("Singleton生成一个实例");
}
//声明public static,可以外部使用类名调用获取实例
public static Singleton getInstance() {
return ourInstance;
}
}
2. 静态变量初始化的方式(饿汉模式)不常用(因为外部使用是使用Singleton.INSTANCE获取,不能屏蔽内部生成对象的实现,例如以后想修改为懒汉模式,调用方也需要修改)
public class Singleton {
//public修饰供外部直接使用类名获取
//static在类加载时调用new Singleton()初始化
//final 防止外部赋值覆盖
public final static Singleton INSTANCE = new Singleton();
private Singleton(){
System.out.println("Singleton生成一个实例");
}
}
3.获取时方法内初始化(懒汉模式)不常用(存在性能问题,因为每次获取单例对象都受到synchronized限制,需排队)
public class Singleton {
//类加载初始化为null
private static Singleton singleton;
//声明为private的构造函数只可内部调用
private Singleton(){
System.out.println("Singleton生成一个实例");
}
//声明public static,可以外部使用类名调用获取实例
//使用synchronized关键字确保多线程情况下也仅生成一个对象
public static synchronized Singleton getInstance(){
if(singleton == null){
//判断singleton为空时,生成对象
singleton = new Singleton();
}
return singleton;
}
}
4.获取时方法内初始化(属于上一个单例模式写法的优化版,修改了synchronized的作用范围)(懒汉模式)常用
public class Singleton {
//类加载初始化为null
private static Singleton singleton;
//声明为private的构造函数只可内部调用
private Singleton(){
System.out.println("Singleton生成一个实例");
}
public static Singleton getInstance(){
//第一次判断singleton是否为空,防止大部分方法请求进入synchronized代码块
if(singleton == null){
synchronized (Singleton.class) {
//第二次判断singleton是否为空,是为了防止多线程情况下,第一次判断均通过后,排队进入静态代码块,多次创建对象
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
5.内部类初始化(懒汉模式)常用
public class SingletonInner {
//声明为private的构造函数只可内部调用,内部类也可以调用
private SingletonInner() {
System.out.println("SingletonInner生成一个实例");
}
//声明public static,可以外部使用类名调用获取实例
public static SingletonInner getInstance() {
//返回内部类持有的INSTANCE(此时加载内部类)
return SingletonIn.INSTANCE;
}
//定义private内部类,外部不可见,在内部类加载时,初始化INSTANCE,类加载机制确保了线程安全
private static class SingletonIn{
private static final SingletonInner INSTANCE = new SingletonInner();
}
}
三、错误示范
1.未使用private修饰构造函数
/**
* 这里不关注生成方式(饿汉、懒汉),所以选择了比较简便的写法
*/
public class SingletonErr {
private static SingletonErr singleton = new SingletonErr();
public static SingletonErr getInstance(){
return singleton;
}
}
外部可以使用new关键字生成 SingletonErr的对象,违背了单例模式的概念
2.存在线程安全问题
/**
* 懒汉模式这以下写法存在线程安全问题
*/
public class SingletonErr {
private static SingletonErr singleton;
private SingletonErr(){
System.out.println("SingletonErr生成一个实例");
}
//多线程并发首次获取时,可能会创建多个实例
public static SingletonErr getInstance1(){
if(singleton == null){
singleton = new SingletonErr();
}
return singleton;
}
//多线程并发首次获取时,判断对象为空这块可能会同时通过,创建对象虽加了synchronized,但不能防止通过空校验的线程多次创建实例
public static SingletonErr getInstance2(){
if(singleton == null){
synchronized (SingletonErr.class){
singleton = new SingletonErr();
}
}
return singleton;
}
}
多线程时,调用getInstance1或者getInstance2可能返回不同的对象,违背了单例模式的概念