一个菜鸡JAVA后端的博客~

设计模式-单例设计模式

定义

  • Ensure a class has only one instance,and provide a global point of access to it(确保某一个类只有一个实例,而且自行实例化并向这个系统提供这个实例)

优点

  • 内存中只有一个实例,减少了内存开支。特别是当一个对象需要频繁的创建销毁时
  • 只生成一个实例,减少系统性能开销。(当一个对象产生需要比较多的资源如读取配置文件的时候,可以在应用启动时产生一个单例对象,然后用永久驻留内存的方式解决(注意垃圾回收机制)

缺点

  • 单例模式一般没有接口,扩展很难(接口对单例模式没有任何意义)
  • 单例模式对测试不利(并行开发环境下,如果单例模式没有完成,是不能够测试的)

使用场景

  • 需求:要求一个类有且仅有一个对象,如果多个对象就会出现不良反应
  • 举例
    • 要求生成唯一序列号的环境
    • 在整个项目中需要一个共享访问点或共享数据
    • 创建一个对象需要消耗的资源过多。如访问IO和数据库等资源
    • 需要定义大量的静态常量和静态方法,如工具类(也可以直接使用static)

分类

通用类图

单例模式通用类图

懒汉式单例

在使用时创建单例对象。

代码

package com.pattern.singleton;

/* 懒汉式单例,在调用的时候再初始化 */
public class SingletonLazy {
    // 构造私有化
    private SingletonLazy() {
    };

    private static SingletonLazy singletonLazy = null;

    // 通过静态方法获取对象
    public static SingletonLazy newInstance() {
        if (singletonLazy == null) {
            singletonLazy = new SingletonLazy();
        }
        return singletonLazy;
    }
    /**
     * 注意: 懒汉式单例会导致在高并发的时候线程不安全。如两个线程A和B 当第一个线程A走到new
     * SingletonLazy的时候,此时正在创建对象,还没获取到对象(创建对象需要时间),然后线程B走到了(singletonLazy ==
     * null)方法,此时判断结果为true,于是就创建了一个SingletonLazy对象然后返回给B,此时A完成了singletonlazy对象的初始化然后返回给A。
     * 这个时候,线程A和线程B的singleton对象实际上并不是同一个对象。
     * 解决方法:
     *  1.使用饿汉式单例(推荐使用)
     *  2.给newInstance加锁。如:public synchronized SingletonLazy newInstance()
     */
}

存在问题

  • 线程不安全(高并发的情况下),如:线程A执行到new的时候,但是还没有获取到对象,线程B执行到 对象==null判断,此时B判断为true于是就会创建一个对象,这时A也创建对象就会导致有两个实例。
    • 解决方法:newInstance方法前或内部加上synchronized关键字,也可以直接使用饿汉式(推荐)。

饿汉式单例

在初始化时候直接创建对象。

代码

package com.pattern.singleton;
/* 饿汉式单例,在初始化时候就创建对象,开发中常用的单例模式 */
public class SingletonEager {
    // 构造私有化
    private SingletonEager(){};
    private static SingletonEager singletonEager = new SingletonEager();
    // 使用静态方法创建对象
    public static SingletonEager newInstance(){
        return singletonEager;
    }
}

有上限的多例

有上限的多例是单例模式的一个变种,通过容器来限制多例的数量。
如:写日志文件,分别写在两个不同的路径。此时就可以使用有上限的多例来为每个路径的日志文件分配一个对象。

代码

package com.pattern.singleton;

import java.util.ArrayList;
/* 有限的多例模式,是单例的一个变种 */
public class LimitMultiton {
    // 指定实例的上限
    private static int MAX_INSTANCE_COUNT = 2;
    // 存放指定数量多例的容器,集合类初始化时最好给定大小,不确定给默认值
    // 此处使用了ArrayList可能导致线程不安全,可以改成vetor
    private static ArrayList<LimitMultiton> instanceList = new ArrayList<>(MAX_INSTANCE_COUNT);
    // 构造私有化
    private LimitMultiton(){};
    // 静态代码块,只执行一次
    static{
        for (int i = 0; i < MAX_INSTANCE_COUNT; i++) {
            instanceList.add(new LimitMultiton());
        }
    }
    // 返回容器中实例的数量
    public static int getInstanceCount(){
        return MAX_INSTANCE_COUNT;
    }
    // 返回实例
    public static LimitMultiton newInstance(int index){
        return instanceList.get(index);
    }
}  

测试代码

package com.pattern;

import java.util.Random;

import com.pattern.singleton.LimitMultiton;
import com.pattern.singleton.SingletonEager;
import com.pattern.singleton.SingletonLazy;

import org.junit.Test;

public class SingletonTest {
    @Test
    public void SingletonEagerTest(){
        SingletonEager singletonEager = null;
        for (int i = 0; i < 10; i++) {
            singletonEager = SingletonEager.newInstance();
            System.out.println(singletonEager);
        }
    }
    @Test
    public void SingletonLazyTest(){
        SingletonLazy singletonLazy = null;
        for (int i = 0; i < 10; i++) {
            singletonLazy = SingletonLazy.newInstance();
            System.out.println(singletonLazy);
        }
    }
    @Test
    public void LimitMultitonTest(){
        LimitMultiton limitMultiton = null;
        for (int i = 0; i < 20; i++) {
            limitMultiton = LimitMultiton.newInstance(new Random().nextInt(LimitMultiton.getInstanceCount()));
            System.out.println(limitMultiton);
        }
    }
}

注意

  • 单例类不要实现Cloneable,否则可以通过对象复制来创建一个新的单例对象(Java中,如果类实现了Cloneable接口,并实现了clone方法,可以直接通过对象复制方式创建一个新的对象)

NOTE: 文章若无特别说明均为原创文章,如果要转载请保留出处!