设计模式之---单例

单例模式的定义与特点

单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。

在计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。

单例模式有 3 个特点:
单例类只有一个实例对象;
该单例对象必须由单例类自行创建;
单例类对外提供一个访问该单例的全局访问点;

单例模式的结构与实现
单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。

下面来分析其基本结构和实现方法。
1. 单例模式的结构
单例模式的主要角色如下。
单例类:包含一个实例且能自行创建这个实例的类。
访问类:使用单例的类。

2. 单例模式的实现
Singleton 模式通常有两种实现形式。
第 1 种:懒汉式单例
该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。代码如下:
public class LazySingleton
{
private static volatile LazySingleton instance=null; //保证 instance 在所有线程中同步
private LazySingleton(){} //private 避免类在外部被实例化
public static synchronized LazySingleton getInstance()
{
//getInstance 方法前加同步
if(instance==null)
{
instance=new LazySingleton();
}
return instance;
}
}

注意:如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。

第 2 种:饿汉式单例
该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。
public class HungrySingleton
{
private static final HungrySingleton instance=new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance()
{
return instance;
}
}

单例模式的应用场景
前面分析了单例模式的结构与特点,以下是它通常适用的场景的特点。
在应用场景中,某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。

项目中使用:

 public class ThreadConfig {

/** 线程池保持ALIVE状态线程数 */
public static final int CORE_POOL_SIZE = 10;

/** 线程池最大线程数 */
public static final int MAX_POOL_SIZE = 40;

/** 空闲线程回收时间 */
public static final int KEEP_ALIVE_TIME = 2000;

/** 线程池等待队列 */
public static final int BLOCKING_QUEUE_SIZE = 1000;

private static ThreadPoolExecutor tpoole;

private ThreadConfig(){
    if(tpoole == null){
        tpoole = new ThreadPoolExecutor(
                CORE_POOL_SIZE,MAX_POOL_SIZE,KEEP_ALIVE_TIME,TimeUnit.MICROSECONDS,
                new LinkedBlockingDeque<Runnable>(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());
    }

}
private static ThreadConfig instance;

public static ThreadConfig getInstance() {
    if (instance == null) {
        instance = new ThreadConfig();
    }
    return instance;
}


public void executeTask(Runnable runnable) {
    tpoole.execute(runnable);
}
}

调用:

final CountDownLatch count = new CountDownLatch(map.size());
        for (Map.Entry<String, List<String>> entry : map.entrySet()) {
            ThreadConfig.getInstance().executeTask(new Runnable() {
                @Override
                public void run() {
                    try {
                      调用业务方法;
                        count.countDown();
                    } catch (Exception e) {
                        logger.warn(ExceptionUtils.getStackTrace(e));
                    }
                }
            });
count.await();

线程安全的懒加载:
public class CBConfig {

private static CyclicBarrier cb;

//使用volatilt关键字修饰,可以阻止CPU指令重排,并保证其可见性
private volatile static CBConfig cbConfig = null;

private ConcurrentHashMap<String,List<String>> map = new ConcurrentHashMap();
//提供get方法以供调用获取唯一实例
public static CBConfig getCbConfig(int threadSize){
     //判读实例是否为null;如果为null则创建实例,否则直接返回
	 //之所以不锁这里,是为了在多线程下提高效率,否则每次只有一个线程可访问获取实例
    if(cbConfig == null){
        //锁住Singleton可以保证同一时间只会有一个线程进入实例化对象,保证实例的唯一性
		//并且在第一次实例化完成后,之后的线程在此调用该方法的时候会在前面的if处过滤掉,不会进入锁中,提高效率
        synchronized (CBConfig.class){
            //双重判断
			//如果在实例还未被创建时候,有多个线程同时进入第一层if,在此处进行二次判断,可避免多次创建实例
            if(cbConfig == null){
                cbConfig = new CBConfig(threadSize);
            }
        }
    }
    return cbConfig;
}
//私有化构造方法,确保不会在其他地方产生新的对象
private CBConfig(int threadSize){
    cb = new CyclicBarrier(threadSize,() ->{
        logger.info("准备查询-----------");
    });
}

public void executeTask() throws Exception{
    cb.await();
}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值