一.单例模式概念
单例模式:为在程序运行期间,仅存在唯一实例
构成:
1.私有化构造函数,不允许其被实例化多次
2.提供全局访问点
二.单例模式-懒汉式和饿汉式两种
1.饿汉式:在调用该单例之前,就已经进行了实例化
public class HungrySingleton {
private static HungrySingleton hungrySingleton = new HungrySingleton();
private String display = "饿汉式";
private HungrySingleton(){
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
public String getDisplay() {
return display;
}
public void setDisplay(String display) {
this.display = display;
}
}
优点:当程序启动时,就已经实例化好了,不会出现线程安全问题
缺点:1.可能会造成系统资源浪费,当我不需要调用该实例时,该实例就已经实例化好了.
2.当饿汉式实例多时,会造成系统启动过慢.
2.懒汉式:只有当用户调用该单例时,才进行实例化
1.懒汉式-线程不安全
public class LazyUnsafeSingleton {
private static LazyUnsafeSingleton lazyUnsafeSingletion = null;
private String display = "懒汉式-不安全";
private LazyUnsafeSingleton() {
}
public static LazyUnsafeSingleton getInstance(){
if (lazyUnsafeSingletion!=null){
lazyUnsafeSingletion = new LazyUnsafeSingleton();
}
return lazyUnsafeSingletion;
}
public String getDisplay() {
return display;
}
public void setDisplay(String display) {
this.display = display;
}
}
优点:当需要调用时,才会去进行实例化
缺点:会产生线程安全问题,当有两个线程同时调用时,会实例两次
2.懒汉式-线程安全
public class LazySafeSingleton {
private static LazySafeSingleton lazySafeSingleton = null;
private String display = "懒汉式-安全";
private LazySafeSingleton() {
}
public static synchronized LazySafeSingleton getInstance(){
if (lazySafeSingleton !=null){
lazySafeSingleton = new LazySafeSingleton();
}
return lazySafeSingleton;
}
public String getDisplay() {
return display;
}
public void setDisplay(String display) {
this.display = display;
}
}
优点:采用同步方法的方式,使线程安全
缺点:1.由于采用同步方法,在方法上加同步锁,同步范围较大,性能会很低
2.多线程来获取实例时,不管该单例有没有实例化,都会阻塞,性能会很低
3.懒汉式-双重检查锁
public class LazyDoubleCheckSingleton {
private static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;
private String display = "懒汉式-安全-双重检查锁机制";
private LazyDoubleCheckSingleton() {
}
public static LazyDoubleCheckSingleton getInstance(){
if (lazyDoubleCheckSingleton == null){
synchronized(LazySafeSingleton.class){
if (lazyDoubleCheckSingleton == null){
lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
}
}
}
return lazyDoubleCheckSingleton;
}
public String getDisplay() {
return display;
}
public void setDisplay(String display) {
this.display = display;
}
}
优点:1.由于采用双重检查机制,当类没有被实例化时,才会进入同步代码块内,进行实例,效率变高
缺点:代码臃肿
4.懒汉式-静态内部类
public class LazyInnerSingleton {
private LazyInnerSingleton() {
}
// 采用静态内部,初始化单例,当单例被使用时,才会进行数据加载
private static class InnerInstance{
private static LazyInnerSingleton lazyInnerSingleton = new LazyInnerSingleton();
}
public static LazyInnerSingleton getInstance(){
return InnerInstance.lazyInnerSingleton;
}
}
执行时序图
优点:采用静态内部类的方式,实例化单例,当单例被调用时,才会实例化
缺点:会生成两个类,而且静态内部类,会存在永久代中
5.懒汉式-枚举方式
public enum RegisterSingleton {
INSTANCE;
}
优点:1.这就是一个单例,直接RegisterSingleton.INSTANCE就能获取该枚举的实例-代码简单
2.由于枚举的特性,不会被反射以及序列化破坏
三.如何保护单例不被破坏?
破坏单例的两种方式:反射 和 序列化
1.在私有化构造函数内,加入判断是否已经实例判断-如果已实例则抛异常---防止反射破坏单例
2.重写readResolve()方法,返回已实例化的对象---防止序列化破坏单例
public class HungryProtectSingleton implements Serializable {
private static final long serialVersionUID = 1L;
private static HungryProtectSingleton hungryProtectSingleton;
static {
hungryProtectSingleton = new HungryProtectSingleton();
}
// 防止反射破坏单例
private HungryProtectSingleton() {
if(hungryProtectSingleton!=null){
throw new RuntimeException("该单例,不允许被反射实列化!");
}
}
public static HungryProtectSingleton getInstance(){
return hungryProtectSingleton;
}
//防止序列化破坏单例
private Object readResolve(){
return hungryProtectSingleton;
}
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
HungryProtectSingleton hungryProtectSingleton = HungryProtectSingleton.class.newInstance();
HungryProtectSingleton instance = HungryProtectSingleton.getInstance();
System.out.println(hungryProtectSingleton);
System.out.println(instance);
}
}