这个专栏 主要分享和记录一些之前我对设计模式的学习和理解。希望可以帮助到之前不了解设计模式的新手
单例模式
首先什么叫单例模式?
就是:
无论在代码的哪个地方,通过相同的方式获取该类的实例,都将获得同一个对象。
那么哪些场景下面 我们需要搞这种 比如获取多次之后 必须包装获取到的是同一个对象呢?
共享资源:当多个对象需要访问和共享同一资源时,使用单例模式可以确保只有一个实例管理和提供该资源,避免资源的重复创建和浪费,例如数据库连接池、线程池等。
配置信息:单例模式可以用于管理全局的配置信息,确保在整个应用程序中配置信息的一致性和可靠性。例如,全局的系统配置、日志配置等。
缓存对象:在需要缓存大量对象时,使用单例模式可以提供一个全局的缓存管理器,确保缓存对象的一致性和可控性。例如,页面缓存、数据缓存等。
日志记录器:使用单例模式可以实现全局唯一的日志记录器,方便在应用程序的各个部分记录日志,并统一管理日志的输出格式和级别。
对象工厂:当需要限制某个类只能有一个实例,并提供全局访问时,可以使用单例模式作为对象工厂。这样可以确保只有一个实例被创建,并提供统一的访问点。
先了解了背景,然后我们再说实现:
为了照顾新手 我们一步一步的讲
我们在java中无时无刻不在创建对象 获取某个类的实例 比如你创建了一个类A:
A a = new A();
可以了这就创建了一个关于class A 的实例对象。假设这个A里面装的就是数据库连接配置的东西
那我再创建一个
A a1 = new A();
现在问题来了 a 和a1 是不是同一个对象?
肯定不是对吧 因为你已经new了2次了。你每new一次 就创建了一个新对象。
所以第一点至少明确了 我们要实现你创建2次 获得的是同一个对象,肯定不能用new的方式。
这时候我们想到了 static 静态修饰。 被static静态修饰的变量 它属于类本身不属于类的实例。它是可以之间类名.调用的
所以我们这样写:
/**
* 饿汉式 单例模式
* */
public class Singleton1 {
//构造方法设为私有
private Singleton1(){}
//创建一个本类的对象
private static Singleton1 singletonInstance = new Singleton1();
//提供一个外界可以访问的方式
public static Singleton1 getInstance(){
return singletonInstance;
}
}
getInstance方法对外提供一个口子,外界可以调用它来创建一个singletonInstance。
这就是饿汉式 单例模式。
这种模式有点浪费 ,浪费在哪里呢?外界还没有调用你的时候 你在类加载里面已经把成员变量Singleton1 给实例化了。
等于是你每次都要实例化它——你每天都要做一顿饺子 不管有没有人来吃 每人来吃 就浪费了
所以我们改进:
/**
* 懒汉式 线程不安全 单例模式
* */
public class Singleton2 {
//构造方法设为私有
private Singleton2(){}
//创建一个本类的对象
private static Singleton2 singletonInstance;
//提供一个外界可以访问的方式
public static Singleton2 getInstance(){
if (singletonInstance==null) {
singletonInstance = new Singleton2();
}
return singletonInstance;
}
}
它调用getInstance方法的时候 我们再创建——你每天只有等来人的时候 才做饺子 来一个做一顿 不浪费。
但是这里牵扯到一个线程安全问题:
你看 比如现在1万个线程同时并发访问getInstance方法,那么就可能出现多个线程一起执行了 if (singletonInstance==null) ,这时候多个线程 都执行到里面 谁都还没结束 没出这个if(){。。。。}。 那么久出现了创建了多个对象。
这就是线程不安全的问题
所以我们把这一块 singletonInstance = new Singleton2(); 给他 直接用悲观锁 锁住。
意味着多线程走到这里的时候 就变成了一个一个一个排队执行。
/**
* 懒汉式 线程安全 单例模式
* */
public class Singleton3 {
//构造方法设为私有
private Singleton3(){}
//创建一个本类的对象
private static volatile Singleton3 singletonInstance;
//提供一个外界可以访问的方式
public static Singleton3 getInstance(){
//如果为null 可能出现线程安全问题
if (singletonInstance==null) {
synchronized (Singleton3.class) {
if (singletonInstance==null) {
singletonInstance = new Singleton3();
}
}
}
//如果不为null 不需要考虑线程安全问题
return singletonInstance;
}
}