Singleton模式即单例模式,故名思意,单例模式会确保任何情况下都绝对只有一个实例,当我们想在程序中表示某个东西只会存在一个的时候,那么就需要单例模式。
示例程序
Singleton 类
public class Singleton {
private static Singleton singleton = new Singleton();
// 构造器是私有的,无法使用new生成实例
private Singleton() {
System.out.println("生成了一个实例");
}
public static Singleton getInstance() {
return singleton;
}
}
Main 类
public class Main {
public static void main(String[] args){
Singleton obj1 = Singleton.getInstance();
Singleton obj2 = Singleton.getInstance();
if(obj1 == obj2){
System.out.println("obj1 与 obj2 是相同的实例");
}else{
System.out.println("obj1 与 obj2 是不同的实例");
}
}
}
运行结果如下:
生成了一个实例
obj1 与 obj2 是相同的实例
Singleton 模式中的角色
Singleton
Singleton模式中只有Singleton这一个角色;Singleton角色中有一个返回实例的static方法,该方法总是会返回同一个实例。
UML
对Singleton模式的思考
单例模式的作用
单例模式的作用其一是为了解决多线程并发访问的问题。
例如:一个系统中可以存在多个预执行任务,但是只能有一个正在工作的任务。例如示例程序:
public class TicketMaker {
private int ticket = 1000;
private static TicketMaker ticketMaker = new TicketMaker();
private TicketMaker(){
}
public static TicketMaker getInstance(){
return ticketMaker;
}
public synchronized int getNextTicketNumber(){
return ticket++;
}
}
上述程序中,我们只希望生成一个TicketMaker实例,并且能够让getNextTicketNumber在多线程的环境下正常工作,所以我们需要将其定义为synchronized方法。
单例模式的作用其二是为了节约系统的内存,提高系统运行的效率,如果对于一种工具类,你需要频繁调用,如果每次调用都要产生新的实例,那么就会在浪费内存的空间,所以单例模式的一个优点就是减少资源消耗,并且能提高程序的运行速度。
一个问题
有单例模式如下:
public class Singleton1 {
private static Singleton1 singleton1 = null;
private Singleton1(){
}
public static Singleton1 getInstance(){
if(singleton1 == null){
singleton1 = new Singleton1();
}
return singleton1;
}
}
为什么上述程序不是严格的Singleton模式 ?
答案:因为在多线程几乎同时调用Singleton.getInstance方法的时候,可能会生成多个实例。
首先将程序改动一下,降低一下处理速度:
public class Singleton1 {
private static Singleton1 singleton1 = null;
private Singleton1(){
try {
Thread.sleep(1000);
}catch (InterruptedException e){
}
}
public static Singleton1 getInstance(){
if(singleton1 == null){
singleton1 = new Singleton1();
}
return singleton1;
}
}
在多线程的环境下,进行程序的测试:
public class Main extends Thread{
public Main(String name){
super(name);
}
public void run(){
Singleton1 obj = Singleton1.getInstance();
System.out.println(getName() + ": obj = " + obj);
}
public static void main(String[] args){
new Main("A").start();
new Main("B").start();
new Main("C").start();
}
}
程序运行的结果为:
C: obj = DesignMode.Singleton.Singleton1@5c13df07
B: obj = DesignMode.Singleton.Singleton1@50564e5e
A: obj = DesignMode.Singleton.Singleton1@5b1a5c4a
可以看到产生了多个不同的实例,为什么会出现这种情况呢?
原因在于,在多线程的环境下,使用如下代码
if(singleton == null){
singleton = new Singleton();
}
进行判断是线程不安全的,如果在使用singleton == null 判断了第一个实例是否为空后,执行了语句,但是在赋值之前,其他的线程也在进行singleton == null的判断,继而创建出了不同的实例。那么如何修改本程序呢?
修改结果如下:
public synchronized static Singleton1 getInstance(){
if(singleton1 == null){
singleton1 = new Singleton1();
}
return singleton1;
}
将getInstance变为 synchronized方法即可。对于这种单例模式:
public class Singleton1 {
private static Singleton1 singleton1 = null;
private Singleton1(){
try {
Thread.sleep(1000);
}catch (InterruptedException e){
}
}
public synchronized static Singleton1 getInstance(){
if(singleton1 == null){
singleton1 = new Singleton1();
}
return singleton1;
}
}
我们称之为懒汉式单例模式,而最开始的那种单例模式则是饿汉式。两者的优缺点为饿汉式为线程安全,调用效率高,但是不能延时加载;懒汉式线程安全,但是因为 getInstance方法为synchronized,所以调用效率不高,但是可以延时加载。其实还有更好的实现单例模式的方法,比如:静态内部类实现单例模式:
public class Singleton2 {
private static class SingletonClassInstance {
private static final Singleton2 instance = new Singleton2();
}
private Singleton2() {
}
public static Singleton2 getInstance() {
return SingletonClassInstance.instance;
}
}
这种实现线程安全,调用效率高,并且可以做到延时加载。