设计模式——单例模式
一、单例模式介绍
单例设计模式,简单来说就是通过一定方法保证某个类在整个系统程序中仅存在一个实例,且各类仅保证一个区的其对象实例的方法。
二、单例模式分类
1、饿汉式
(1)静态常量
(2)静态代码块
2、懒汉式
(1)原版
(2)双重检测版
(3)线程安全版
3、静态内部类
4、注册式
(1)枚举式
(2)容器式
三、单例模式代码实例
1、饿汉式
1)静态常量
//1.饿汉式(静态变量)
class Singleton{
//1.构造器私有化,外部不能new
private Singleton(){}
//2.本类内部创建对象实例
private final static Singleton INSTANCE = new Singleton();
//3.提供一个公有的静态方法返回实例对象
public static Singleton getInstance(){
return INSTANCE;
}
}
优缺点
(1)优点:该方法基于类装载机制,避免了多线程同步的问题
(2)缺点:没有达到懒加载的效果,即若从未使用该实例,则造成内存的浪费。
2)静态块
//2.饿汉式(静态变量)
class Singleton{
//1.构造器私有化,防止外部new
private Singleton(){}
//2.本类内部创建对象实例
private static Singleton instance;
static{ //在静态代码块中,创建对象实例
instance = new Singleton();
}
//3.提供一个公有的静态方法,返回实例对象
public static Singleton getInstance(){
return instance;
}
}
优缺点
(1)优点:该方法基于类装载机制,避免了多线程同步的问题
(2)缺点:没有达到懒加载的效果,即若从未使用该实例,则造成内存的浪费。
2、懒汉式
1)原版
//3.懒汉式(原版)
class Singleton{
private static Singleton instance;
private Singleton(){}
//提供一个静态的公有方法(静态工厂),只有调用方法时才去创建instance
//即懒加载 线程不安全
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
...
public static void main(String[] args) throws Exception{
// 构造器私有化,外部不能new
//两者HashCode会有不同结果,即产生了多个实例
//Singleton singleton = new Singleton();
Thread a= new Thread(()->{
for(int i = 0; i< 3; i++){
Singleton instance = Singleton.getInstance();
// System.out.println(i);
System.out.println(instance.hashCode());
}
},"lazylost");
a.start();
// a.join();
Thread b = new Thread(()->{
for(int i = 0; i< 3; i++){
Singleton instance1 = Singleton.getInstance();
//System.out.println(i);
System.out.println(instance1.hashCode());
}
},"lazylost2");
b.start();
}
}
优缺点
(1)优点:该方法起到了懒加载的效果,即不会造成内存浪费
(2)缺点:多线程下,线程A进入到if(singleton == null)判断语句后,线程B也通过该判断语句,则产生了多个实例。
2)双重检测版
//4.懒汉式(双重检测)
class Singleton{
private static Singleton instance;
private Singleton(){}
//提供一个静态的公有方法(静态工厂),只有调用方法时才去创建instance
//即懒加载 同步代码块
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
优缺点
(1)优点:该方法起到了懒加载的效果,即不会造成内存浪费;双重检测机制保障一定程度上的线程安全;同步代码块而不是同步方法保证了程序性能。
(2)缺点:JVM编译器的指令重排将有可能导致生成一个未初始化的实例。
3)线程安全版
//5.懒汉式(线程安全)
class Singleton{
private static volatile Singleton instance;
private Singleton(){}
//提供一个静态的公有方法(静态工厂),只有调用方法时才去创建instance
//即懒加载 同步代码块
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
优缺点
(1)优点:volatile关键字保证JVM的执行顺序;懒加载,避免内存浪费;双重检测和同步块保证线程安全。
(2)缺点:深克隆会破坏该单例模式,解决方法请参考:深克隆破坏单例模式
3、静态内部类
//6.静态内部类
class Singleton{
private Singleton(){
if(SingletonInstance.INSTANCE != null){
throw new RuntimeException("不允许创建多个实例");
}
}
private static class SingletonInstance{
private static final Singleton INSTANCE = new Singleton();
}
//static保证了单例的共享空间,final保证该方法不被重写
public static final Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
...
public static void main(String[] args) throws Exception{
Constructor con = Singleton.class.getDeclaredConstructor();
con.setAccessible(true);
Singleton s1 = (Singleton) con.newInstance();
Singleton s2 = (Singleton) con.newInstance(); //抛出异常
}
优缺点
(1)优点:利用静态内部类避免了线程不安全,同时实现了懒加载,效率高;在构造方法中的限制导致反射无法破坏该单例
【注】
若未在构造方法中抛出异常,则反射机制将破解该单例
4、注册式
1)枚举类
enum SingletonEnum{
INSTANCE;
public static SingletonEnum getInstance(){
return INSTANCE;
}
}
public static void main(String[] args) throws Exception{
SingletonEnum instance = SingletonEnum.INSTANCE;
System.out.println(instance.hashCode());
SingletonEnum instance1 = SingletonEnum.INSTANCE;
System.out.println(instance.hashCode());
Constructor con = SingletonEnum.class.getDeclaredConstructor();
con.setAccessible(true);//会抛出异常“java.lang.NoSuchMethodException”
}
}
优缺点
(1)优点:利用enum语法糖,JVM会组织反射获取枚举类的私有构造方法。
(2)缺点:没有达到懒加载的效果,即若从未使用该实例,则造成内存的浪费。
2)容器式
class Singleton{
//1.构造器私有化,外部不能new
private Singleton(){}
private static Map<String, Object> ioc = new ConcurrentHashMap<String,Object>();
public static Object getBean(String className){
sychronized(ioc){
if(!ioc.containKey(className)){
Object obj = null;
try{
obj = Class.forName(className).newInstance();
ioc.put(className, obj);
}catch(Exception e){
e.printStackTrace();
}
return obj;
}else{
return ioc.get(className);
}
}
}
}
优缺点
(1)优点:便于管理
(2)缺点:非线程安全
四、单例模式在实际中的运用
Spring中的单例模式
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
/** Strategy for creating bean instances */
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
/** Cache of unfinished FactoryBean instances: FactoryBean name --> BeanWrapper */
private final Map<String, BeanWrapper> factoryBeanInstanceCache =
new ConcurrentHashMap<String, BeanWrapper>(16);
...
//饿汉式
protected InstantiationStrategy getInstantiationStrategy() {
return this.instantiationStrategy;
}
...
//容器式
private FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
synchronized (getSingletonMutex()) {
BeanWrapper bw = this.factoryBeanInstanceCache.get(beanName);
if (bw != null) {
return (FactoryBean<?>) bw.getWrappedInstance();
}
if (isSingletonCurrentlyInCreation(beanName)) {
return null;
}
Object instance = null;
try {
// Mark this bean as currently in creation, even if just partially.
beforeSingletonCreation(beanName);
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
instance = resolveBeforeInstantiation(beanName, mbd);
if (instance == null) {
bw = createBeanInstance(beanName, mbd, null);
instance = bw.getWrappedInstance();
}
}
finally {
// Finished partial creation of this bean.
afterSingletonCreation(beanName);
}
FactoryBean<?> fb = getFactoryBean(beanName, instance);
if (bw != null) {
this.factoryBeanInstanceCache.put(beanName, bw);
}
return fb;
}
}
...
}
JDK中的单例模式
public class Runtime {
private static Runtime currentRuntime = new Runtime();
/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class <code>Runtime</code> are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the <code>Runtime</code> object associated with the current
* Java application.
*/
public static Runtime getRuntime() {
return currentRuntime;
}
...
}
五、单例模式的使用场景
1)需要频繁进行创建和销毁的对象
2)创建常使用的重量级对象(耗时耗资源)
3)工具类对象
4)频繁访问数据库或文件的对象(session工厂)