二十三种设计模式——单例模式

前页:设计模式总述


单例模式是java中最简单的一种设计模式。所谓单例模式,就是整个程序只允许有一个类的实例,并且向整个系统中提供此事例。有点像C++中被定义为static的变量。

比如当我们某个程序只有一个关于打印机的端口,那么我们必须将这个端口设计为单例的,因为如果当多台电脑使用这个打印机的时候,只允许第一台电脑对此进行操作,否则打印机将引起混乱,

  • 单例模式特点
  1. 单例类只能有一个实例。
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例。
  • 优点:
  1. 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
  2. 避免对资源的多重占用(比如写文件操作)。
  • 缺点:
  1. 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
  • 使用场景:
  1. 要求生产唯一序列号。 
  2. WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  3. 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等 

说了不少,但是还是不清楚什么是单例模式。作为程序猿,我们更直观的是通过代码了解一个模式。

在说代码之前,我们要说说单例模式的前提内容:

  • java锁机制

上一种通俗意义上的单例模式代码:

public class SingleClass {

	//创建对象
	private static SingleClass singleClass = new SingleClass();
	//设置为私有成员函数,无法在类外被实例化
	private SingleClass(){}
	//设置唯一获取singleClass对象的方法
	public static SingleClass getSingleClass(){
		return singleClass;
	}
	//设置方法表明实例化该对象
	public void show(){
		System.out.println("方法已被实例化");
	}
}
public class SingleMode {
	public static void main(String[] args) {
		//会报错,由于这种实例化被设置为私有函数,不被允许类外访问
		//SingleClass singleClass = new SingleClass();
		SingleClass singleClass = SingleClass.getSingleClass();
		//调用函数
		singleClass.show();
	}
}

 


单例模式的多种实现方式

  • 第一种实现方式:懒汉式,线程不安全
public class SingleClass {
	private static SingleClass singleClass;
	private SingleClass() {}
	public static SingleClass getSingleClass() {
		if (singleClass == null) {
			singleClass = new SingleClass();
		}
		return singleClass;
	}
}

由于上述代码没有对SingleClass加锁,因此线程不安全。

  • 第二种实现方式:懒汉式,线程安全
public class SingleClass {
	private static SingleClass singleClass;
	private SingleClass() {}
	public static synchronized SingleClass getSingleClass() {
		if (singleClass == null) {
			singleClass = new SingleClass();
		}
		return singleClass;
	}
}

对方法getSingleClass加锁,保证每次只能有一个线程访问该方法,可以用于多线程。但是也由于上锁之后,该方法效率降低。适用于不频繁调用个SingleClass的程序。

  • 第三种实现方式:饿汉式,线程安全
public class SingleClass {
	private static SingleClass singleClass = new SingleClass();
	private SingleClass() {}
	public static SingleClass getSingleClass() {
		return singleClass;
	}
}

该方法由于没有加锁,所以执行效率会比较高,但是类在加载的时候会初始化,造成内存浪费。

那么这个方法是线程安全的么?当然是。

这涉及到JVM的一些内容,在JVM中,类加载初始化时就创建好一个静态的对象供外部使用。因此本身就是线程安全的。(唉,不会的东西太多了,课程还复杂,有时间再看看《深入理解JAVA虚拟机》吧)

  • 第四种实现方式:双检锁/双重校验锁(DCL,即 double-checked locking)
public class SingleClass {
	private static volatile SingleClass singleClass;
	private SingleClass() {}
	public static SingleClass getSingleClass() {
		if (singleClass == null) {
			synchronized (SingleClass.class) {
				if (singleClass == null) {
					singleClass = new SingleClass();
				}
			}
		}
		return singleClass;
	}
}

volatile关键字和synchronized关键字区别就是:volatile不具备原子性。因此多线程访问volatile修饰时,若未执行完将会回滚。

该实现方式利用了双重锁,当singleClass为空的时候,锁住该类,进行对象的初始化。线程安全,且在进入实例化方法的时候再对该类上锁,提升了程序的运行效率。

  • 第五种实现方式:登记式/静态内部类​​​​​​​
public class SingleClass {
	private static class SingletonHolder {
		private static final SingleClass singleClass = new SingleClass();
	}
	private SingleClass() {}
	public static final SingleClass getSingleClass() {
		return SingletonHolder.singleClass;
	}
}

这种方式,利用public static final 关键字,在显式调用getSingleClass方法的时候,将类和变量初始化并装载在常量区(即不可被改变)。因此这条特性决定了多线程的安全。

  • 第六种实现方式:枚举
public enum SingleClass{
	singleClass;
	public void method(){
		System.out.println(singleClass+"枚举最简单!");
	}
}

public class SingleMode {
	public static void main(String[] args) {
		SingleClass singleClass =SingleClass.values()[0];
		singleClass.method();
	}
}

虎躯一震······这也太简单了吧。。他自动支持序列化机制,绝对防止多次实例化。

singleClass是一个常量,enum本身也是线程安全的,因此也不存在被改写的情况。。。。


有一些地方待更新!

你可能感兴趣的:(设计模式,单例模式)