单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例模式:
1、保证一个类仅有一个实例,并提供一个访问它的全局访问点;
2、当一个全局使用的类被频繁的创建和销毁;或者你要控制实例的数目,节省系统资源的时候,可以考虑使用单例模式;
3、实现:判断系统是否存在这个单例,如果存在则返回,否则,创建;
4、构造函数私有;
注意:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
单例模式的几种实现方式:
1、懒汉式(lazy loading)
package com.dfcDemo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 第一次调用才初始化,避免内存浪费。
* 但是必须加锁 synchronized 才能保证单例,但加锁会影响效率。
* getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)
* @author lmb
*
*/
public class SingletonLazy {
private static Log logger = LogFactory.getLog(SingletonLazy.class);
private static SingletonLazy instance;
private SingletonLazy(){
logger.info("singleton is constructing");
}
public static synchronized SingletonLazy getInstance(){
if(instance == null){
instance = new SingletonLazy();//用到该类实例的时候再去初始化
}
return instance;
}
public static void main(String[] args) {
logger.info("main方法开始执行---");
//同时开启100个线程去调用getInstance()方法
for (int i = 0; i <= 100; i++) {
new Thread(new Runnable(){
@Override
public void run() {
SingletonLazy.getInstance();
}
}).start();
}
logger.info("main方法执行结束---");
}
}
运行结果:
2016-03-17 11:16:04 INFO com.dfcDemo.SingletonLazy - main方法开始执行---
2016-03-17 11:16:04 INFO com.dfcDemo.SingletonLazy - singleton is constructing
2016-03-17 11:16:04 INFO com.dfcDemo.SingletonLazy - main方法执行结束---
2、饿汉式
package com.dfcDemo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 类加载时就初始化,浪费内存
* @author lmb
*
*/
public class SingletonHungry {
private static Log logger = LogFactory.getLog(SingletonHungry.class);
private static SingletonHungry instance = new SingletonHungry();//首次加载该类的时候就初始化
private SingletonHungry(){
logger.info("singleton is constructing");
}
public static SingletonHungry getInstance(){
return instance;
}
public static void main(String[] args) {
logger.info("main方法开始执行---");
//同时开启100个线程去调用getInstance()方法
for (int i = 0; i <= 100; i++) {
new Thread(new Runnable(){
@Override
public void run() {
SingletonHungry.getInstance();
}
}).start();
}
logger.info("main方法执行结束---");
}
}
运行结果:
2016-03-17 11:16:23 INFO com.dfcDemo.SingletonHungry - singleton is constructing
2016-03-17 11:16:23 INFO com.dfcDemo.SingletonHungry - main方法开始执行---
2016-03-17 11:16:23 INFO com.dfcDemo.SingletonHungry - main方法执行结束---
3、双检锁/双重校验锁(DCL,即 double-checked locking)
实现原理:我们不让线程每次都加锁,而只是在实例未被创建的时候再加锁。同时也能保证多线程的安全,对于instance存在的情况就直接返回;当instance不存在,并且同时有两个线程调用getInstance()方法时,它们都可以通过第一重instance == null判断,然后由于synchronizd锁机制,两个线程只有一个能进入,另一个在外排队等候,必须要其中的一个进入并出来之后另一个才能进入。如果没有了第二重的instance == null的判断,则第一个线程创建了实例,第二个线程还是可以载继续创建实例的。
package com.dfcDemo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 这种方式采用双锁机制,安全且在多线程情况下能保持高性能;
* getInstance() 的性能对应用程序很关键
* @author lmb
*
*/
public class SingletonDCL {
private static Log logger = LogFactory.getLog(SingletonDCL.class);
/**
* 用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最新的值,
* 即jvm虚拟机只是保证从主内存加载到线程工作内存的值是最新的;
*/
private volatile static SingletonDCL instance;
private SingletonDCL(){
logger.info("singleton is constructing");
}
public static SingletonDCL getInstance(){
if (instance == null) {
synchronized (SingletonDCL.class) {
if (instance == null) {
instance = new SingletonDCL();//用到该类实例的时候再去初始化
}
}
}
return instance;
}
public static void main(String[] args) {
logger.info("main方法开始执行---");
//同时开启100个线程去调用getInstance()方法
for (int i = 0; i <= 100; i++) {
new Thread(new Runnable(){
@Override
public void run() {
SingletonDCL.getInstance();
}
}).start();
}
logger.info("main方法执行结束---");
}
}
运行结果:
2016-03-17 11:30:55 INFO com.dfcDemo.SingletonDCL - main方法开始执行---
2016-03-17 11:30:55 INFO com.dfcDemo.SingletonDCL - singleton is constructing
2016-03-17 11:30:55 INFO com.dfcDemo.SingletonDCL - main方法执行结束---
比较:
由于饿汉式,即静态初始化的方式,它是类一加载就实例化的对象,所以要提前占用系统资源。而懒汉式,又会面临着多线程访问的安全性问题,需要做双重锁定这样的处理才可以保证安全。所以,到底是用哪一种方式取决于实际的需求。
实用类与单例类比较:
实用类通常也会采用私有化的构造方法来避免其有实例,但它们还是有很多不同的:
1、实例类不保存状态,仅提供一些静态方法或静态属性,而单例类是有状态的;
2、实用类不能用于继承和多态,而单例类虽然实例唯一,但是可以有子类来继承;
3、实用类是一些方法属性的集合而单例却有着唯一的对象实例。