1、单例设计模式介绍
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。一个对象。
注意:
- 1、单例类只能有一个实例
- 2、单例类必须自己创建自己的唯一实例
- 3、单例类必须给所有其他对象提供这一实例
2、几种实现单例的方式
1、懒汉式,线程不安全
懒汉式,就是用到的时候再加载,如果不加锁,多线程环境下会出现多个实例,算不上单例,一般不用
/**
* 懒汉式,线程不安全,因为没有加锁。
*/
public class Single1 {
private static Single1 single1;
public static Single1 getInstance() {
if (single1 == null) {
return new Single1();
}
return single1;
}
private Single1() {
if (single1 != null) {
throw new RuntimeException("不允许私自创建该类的实例");
}
}
}
2、懒汉式,线程安全
在懒汉式上的基础上,对创建实例的方法加了个锁,防止多线程环境下出现多个实例,但效率低。
/**
* 懒汉式,线程安全,创建实例方法加了锁。
*/
public class Single2 {
private static Single2 single1;
public static synchronized Single2 getInstance() {
if (single1 == null) {
return new Single2();
}
return single1;
}
private Single2() {
if (single1 != null) {
throw new RuntimeException("不允许私自创建该类的实例");
}
}
}
3、饿汉式,线程安全
饿汉式单例基于classLoader类加载机制(双亲委派加载模式,一个类只会被加载一次),在类加载的时候就初始化,避免了多线程问题。但是让费内存,容易造成垃圾对象。
/**
* 饿汉式,线程安全
*/
public class Single3 {
private static Single3 single3 = new Single3();
public static synchronized Single3 getInstance() {
return single3;
}
private Single3() {
if (single3 != null) {
throw new RuntimeException("不允许私自创建该类的实例");
}
}
}
4、DCL双重锁校验
这种方式采用双锁机制,安全且在多线程情况下能保持高性能
/**
* 饿汉式,线程安全
*/
public class Single4 {
//这里需要加上volatile关键字,防止指令重排序,同时采用懒加载机制,释放内存
private static volatile Single4 single4;
public static Single4 getInstance() {
if (single4 == null) {
//判断对象是否以及实例化过,没有则进入加锁代码块,此处可能有多个线程同时进来,等待类对象锁
synchronized (Single4.class) {
// 获取类对象锁,其他线程在外等待,其他线程进来再次判断,如果对象实例化了,则不需要再实例化
if (single4 == null) {
single4 = new Single4();
}
}
}
return single4;
}
private Single4() {
if (single4 != null) {
throw new RuntimeException("不允许私自创建该类的实例");
}
}
}
5、登记式/静态内部类
/**
* 登记式/静态内部类
*/
public class Single5 {
//静态内部类,采用类加载机制实例化对象
private static class single5Holler{
private final static Single5 SINGLE_5 = new Single5();
}
private Single5(){}
public static Single5 getInstance(){
return single5Holler.SINGLE_5;
}
}
6、枚举
public enum EnumSingleton {
INSTANCE;
public EnumSingleton getInstance(){
return INSTANCE;
}
}
public class User {
//私有化构造函数
private User(){ }
//定义一个静态枚举类
static enum SingletonEnum{
//创建一个枚举对象,该对象天生为单例
INSTANCE;
private User user;
//私有化枚举的构造函数
private SingletonEnum(){
user=new User();
}
public User getInstnce(){
return user;
}
}
//对外暴露一个获取User对象的静态方法
public static User getInstance(){
return SingletonEnum.INSTANCE.getInstnce();
}
}
public class Test {
public static void main(String [] args){
System.out.println(User.getInstance());
System.out.println(User.getInstance());
System.out.println(User.getInstance()==User.getInstance());
}
}
3、应用场景
1.需要频繁实例化然后销毁的对象。
2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
3.有状态的工具类对象。
4.频繁访问数据库或文件的对象。
具体应用:
①java的JDK中枚举就是把一些常量实例化为唯一的对象,还有堆中的class对象(类加载器根据类的全限定名把.class的二进制字节码代表的静态存储结构转化为方法区的运行时数据结构,然后根据方法区的类型区生成class对象),JVM中一个类只能存在一个class对象。还有数据库连接池,线程池也是单例的。
②在Spring中把对象封装成Bean,Spring的Bean有一个属性scope。而scope有个参数就是singleton,即单例模式。当scope属性为singleton时,spring容器中就只会存在一个Bean实例。这个唯一性是靠唯一ID来确保的,所以意味着也会存在同一个类不同ID的Bean实例。
③分布式Session中常用就一个全局缓存(如Redis)存储Session对象,多个Web应用共享这个Session对象,这个也是单例。