定义
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意事项:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
- 构造函数是私有的。
- getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。
优点:
- 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
- 避免对资源的多重占用(比如写文件操作)。
缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
代码:
SingleObject.java
public class SingleObject {
//创建 SingleObject 的一个对象
private static SingleObject instance = new SingleObject();
//让构造函数为 private,这样该类就不会被实例化
private SingleObject(){}
//获取唯一可用的对象
public static SingleObject getInstance(){
return instance;
}
public void showMessage(){
System.out.println("Hello World!");
}
}
SingletonPatternDemo.java
public class SingletonPatternDemo {
public static void main(String[] args) {
//不合法的构造函数
//编译时错误:构造函数 SingleObject() 是不可见的
//SingleObject object = new SingleObject();
//获取唯一可用的对象
SingleObject object = SingleObject.getInstance();
//显示消息
object.showMessage();
}
}
单例模式的多种模式:
单例模式的实现有多种方式,常用的有两种,如下所示:
饿汉式,线程安全
单例类的实例在加载的时候就被初始化。
描述:这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton () {
}
public static Singleton getInstance() {
return instance;
}
}
懒汉式,线程不安全
单例类的实例在第一次被引用时候才被初始化。
public class Singleton {
private static Singleton instance=null;
private Singleton() {
}
public static Singleton getInstance(){
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
多线程下线程安全的解决方案:同步锁
同步锁的基本知识:
对线程加锁用的synchronized关键字,这个关键字的用法主要也分为两种:
一种是加在方法名之前,形如:synchronized methodeName(params){……};
二是声明同步代码块,形如:synchronized(this){……};
下面是对懒汉式单例类加上线程同步的实现:
1、在方法名上加同步锁synchronized关键字:
public class Singleton {
private static Singleton instance=null;
private Singleton() {
}
public synchronized static Singleton getInstance(){
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这种方式效率比较低,性能不是太好,不过也可以用,因为是对整个方法加上了线程同步,其实只要在new的时候考虑线程同步就行了,这种方法不推荐使用。
2、同步代码块:
public class Singleton {
private static Singleton instance;
private final static Object syncLock = new Object();
private Singleton() {
}
public static Singleton getInstance(){
if (instance == null) {
synchronized (syncLock) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
synchronized同步块括号中的锁定对象是采用的一个无关的Object类实例,而不是采用this,因为getInstance是一个静态方法,在它内部不能使用未静态的或者未实例的类对象,因此也可以用下面的方法来实现:
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance(){
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}