单例模式是开发模式中最简单,最易于理解的一种模式。简单地说,它指的就是始终保持一个实例的意思。但是,Java的类是可以穿件多个实例的,那么,怎么实现呢?
顾名思义,单例模式就是只有一个实例。单例模式确保某一个类只有一个实例,这个类称为单例类,单例模式有3个要点:
①是某个类只能有一个实例;
②它必须自行创建这个实例;
③是它必须自行向整个系统提供这个实例。例如,一些资源管理器常常设计成单例模式。
在计算机系统中,需要管理的资源有很多,例如每台计算机可以有若干个打印机,但只能有一个打印控制器,以避免两个打印作业同时输出到打印机中,每台计算机可以有若干传真卡,但是只应该有一个软件负责管理传真卡,以避免出现两份传真作业同时传到传真卡的情况。
总之,选择单例模式就是为了避免不一致状态。
首先看一个经典的单例模式实现:
public class Singleton {
private static Singleton uniqueInstance = null;
private Singleton() {
// Exists only to defeat instantiation.
}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。(事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)
但是以上实现没有考虑线程的安全性,所谓线程安全性,就是如果你的代码所在的进程里有多个线程在同时运行,而这些线程可能同时会运行这段代码,如果每次运行的结果和单线程运行的结果一致,而且他们的变量的值也和预期的是一样的,就是线程安全的。或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。显然以上实现并不满足线程安全的要求,在并发环境下很可能出现多个Singleton实例。
Java中实现单例模式一般要注意以下几点:
- 私有的构造方法,保证外部无法创建类实例。
- 私有的静态的类型引用。因为静态的就可以保证只有一个变量引用。
- 提供获取实例的方法。方法名一般为getInstance()。
单例模式通常有两种实现方式:懒汉式和饿汉式
1,懒汉式
对于懒汉式来说,一般需要在getInstance()方法中首先判断实例是否为空,也就是第一次访问的时候才会进入该if语句,然后再返回该实例,例如下面的代码:
private static lanhanObject obj = null;
public static lanhanObject getInstance(){
if(obj==null)
obj = new lanhanObject();//创建新的
return obj;
}
懒汉式有一个缺点,他可能会使线程不安全而无法保证100%的单例,例如,当线程A在以上代码的第4行(进入if语句,创建实例之前)暂停了,此时线程B进入了实例。因此,为了保证懒汉式能做到100%的单例,还需要为getInstance( )方法加上synchronized关键字以保证线程同步,例如如下的代码:
public static synchronized lanhanObject getInstance(){//获取实例方法,保证同步
if(obj == null){
obj = new lanhanObject();
}
return obj;
}
synchronized 关键字也可以运用在方法体中,用同步代码块的形式来保证线程同步,不一定用同步方法。
2,饿汉式
对于饿汉式来说,需要做就是把new 语句写在引用变量定义的地方,然后getInstance( )直接返回就可以了,例如下面的代码:
private static ehanObject obj = new ehanObject(); //静态的类型引用
public static ehanObject getInstance(){ //获取实例方法
return obj;
}
饿汉式不存在线程安全的问题,但是他可能会造成资源浪费的情况。因为,实例会在类加载的时候,随着静态变量的初始化而创建,但是有的时候并不会使用该实例,那么它的创建就有一些浪费了,如果实例比较庞大的话,会影响程序的性能。
总之,懒汉式和饿汉式没有绝对的优劣,主要 根据具体的情况来决定。
package com.leetch.java
//饿汉式 单例 优点:实现简单;缺点:在不需要的时候,白创建了对象,造成资源的浪费
class ConnectionPoolA {
private static ConnectionPoolA cPoolA = new ConnectionPoolA(); // 创建实例
private ConnectionPoolA() {} //私有构造方法
public static ConnectionPoolA getConnectionPoolA() {
return cPoolA;
}
}
//懒汉式 单例 优点:需要对象的时候才创建;缺点:线程不安全
class ConnectionPoolB{
private static ConnectionPoolB cPoolB;
private ConnectionPoolB(){}
public static synchronized ConnectionPoolB getconneConnectionPoolB(){
if(cPoolB==null){
cPoolB = new ConnectionPoolB();
}
return cPoolB;
}
}
public class SinglObjectParttern{
public static void main(String args[]){
ConnectionPoolA cp = ConnectionPoolA.getConnectionPoolA(); //创建1
ConnectionPoolB cp2 = ConnectionPoolB.getconneConnectionPoolB(); //创建2
System.out.println(cp == cp2);
}
}
因为单例模式下,得到的始终是同一个对象,因此,以上代码的运行结果是 :
true