(三)设计模式——单例模式

单例模式(singleton)


描述:

一个类只有一个实例,并且为它提供一个全局访问点

场景:

在操作系统中,打印池是一个用于管理打印任务的应用程序,通过打印池用户可以删除、中止或者改变任务的优先级,在一个系统中只允许运行一个打印池对象,如果重复创建打印池则抛出异常。现使用单例模式来模拟实现打印池的设计,请根据类图编程实现该系统,并写出相应Java代码

在这里插入图片描述

java代码

package org.crudboy;

/** lazy **/
class PrintSpoolerSingleton
{
    private static PrintSpoolerSingleton instance = null;
    private PrintSpoolerSingleton() {} // 此处不可省略

    public static PrintSpoolerSingleton getInstance() throws PrintSpoolerException {
        if (instance == null) {
            System.out.println("创建打印池!");
            instance = new PrintSpoolerSingleton();
        } else {
            throw new PrintSpoolerException("打印池正在工作中!");
        }
        return instance;
    }

    public void manageJobs() {
        System.out.println("管理打印任务!");
    }
}

class PrintSpoolerException extends Exception {
    public PrintSpoolerException(String message) {
        super(message);
    }
}

public class Singleton {
    public static void main(String[] args) {

        PrintSpoolerSingleton ps1, ps2;
        try {
            ps1 = PrintSpoolerSingleton.getInstance();
            ps1.manageJobs();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        System.out.println("-----------------------------");
        try {
            ps2 = PrintSpoolerSingleton.getInstance();
            ps2.manageJobs();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

为保证对象的全局唯一,我们只需要把该对象属性设置为static即可。这样该对象会在第一次访问时被创建(lazy模式)

需要强调的是,一定要显示把类的构造访问声明为private,否则类会自动生成修饰符public的构造方法,这样外部调用者就可以通过new创建多个对象。

 

单例模式在java中几种的写法


饿汉模式

饿汉模式在类被加载时就对该类进行实例化

public class Singleton {
	private static Singleton singleton = new Singleton();
	private Singleton() {}
	public static Singleton getInstance() {
		return singleton;
	}
}

 

懒汉模式(lazy)

我们需要重点关注的懒汉模式,懒汉模式只有在单例对象第一被使用时,才会进行实例化。
 

1. 线程不安全写法

public class Singleton {
	private static Singleton singleton = null;
	private Singleton() {}
	public static Singleton getInstance() {
		if (singleton == null) {
			singleton = new Singleton();  // 可能多次执行
		}
		return singleton;
	}
}

在多线程并发执行下,可能出现在通过判断条件后线程切换,导致多次执行创建语句。
 

2. synchronized加锁

public class Singleton {
	private static Singleton singleton = null;
	private Singleton() {}
	public synchronized static Singleton getInstance() {
		if (singleton == null) {
			singleton = new Singleton();
		}
		return singleton;
	}
}

通过给类方法加上类锁,这样保证了同一时刻只有一个线程访问该方法。
 

3. 双检锁(double checking lock) + volatile

虽然在第2点中保证了线程安全,但也存在一个问题:锁的力度太大了。

我们可以发现不但创建对象的过程被锁住,获取对象的过程也被锁住了。于是可以使用dcl进行优化

public class Singleton {
	private volatile static Singleton singleton = null;
	private Singleton() {}
	public static Singleton getInstance() {
		if (singleton == null) {
			synchronized(this.class) { // 只有在判断对象未被创建时,才上锁
				if (singleton == null) {
					singleton = new Singleton();
				}
			}
		}
		return singleton;
	}
}

为什么单例对象要用volatile修饰呢?

通过volatile,禁止指令重排,防止获取到未初始化的对象。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值