java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。
单例模式有以下特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。
一、 饿汉式单例
public class Singleton{
private Singleton(){}
private static final Singleton instance = new Singleton();
pubic static Singleton getInstance(){
return instance;}
}
饿汉式是典型的空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。
一、 懒汉式单例
public class Singleton{
private Singleton(){}
private static final Singleton instance = null;
pubic static synchronized Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instalce;
}
}
懒汉式是典型的时间换空间,就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间
由于懒汉式的实现是线程安全的,这样会降低整个访问的速度,而且每次都要判断。那么有没有更好的方式实现呢?
2. 双重检查加锁
可以使用“双重检查加锁”的方式来实现,就可以既实现线程安全,又能够使性能不受很大的影响。那么什么是“双重检查加锁”机制呢?
所谓“双重检查加锁”机制,指的是:并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这是第一重检查,进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。
“双重检查加锁”机制的实现会使用关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。
public class Singleton{
private Singleton(){}
private static final Singleton instance = null;
//先检查实例是否存在,如果不存在才进入下面的同步块
pubic static Singleton getInstance(){
if(instance == null){
//同步块,线程安全的创建实例
synchronized(Singleton.class){
//再次检查实例是否存在,如果不存在才真正的创建实例
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
3. 静态内部类
public class Singleton {
private Singleton(){}
/**
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
* 没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
*/
private static class SingletonHolder{
/**
* 静态初始化器,由JVM来保证线程安全
*/
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
(1)什么是类级内部类?
* 简单点说,类级内部类指的是,有static修饰的成员内部类。如果没有static修饰的成员式内
* 部类被称为对象级内部类。
* (2)类级内部类相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此
* 可以直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。
* (3)类级内部类中,可以定义静态的方法。在静态方法中只能引用外部类中的静态成员方法或变量。
* (4)类级内部类相当于其外部类的成员,只有在第一次被使用的时候才会被装载。
一、 懒汉式与饿汉式区别
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,
而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。
至于1、2、3这三种实现又有些区别,
第1种,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的,
第2种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗
第3种,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗
二、 范例
public class TestSingleton {
String name = null;
private TestSingleton() {
}
private static TestSingleton instance = null;
public static TestSingleton getInstance() {
if (instance == null) {
synchronized (TestSingleton.class) {
if (instance == null) {
instance = new TestSingleton();
}
}
}
return instance;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void printInfo() {
System.out.println("the name is " + name);
}
}
public class TMain {
public static void main(String[] args){
<span style="font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 18px; background-color: rgb(245, 250, 226);">TestSingleton</span> ts1 = TestSingleton.getInstance();
ts1.setName("jason");
<span style="font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 18px; background-color: rgb(245, 250, 226);">TestSingleton</span> ts2 = TestSingleton.getInstance();
ts2.setName("0539");
ts1.printInfo();
ts2.printInfo();
if(ts1 == ts2){
System.out.println("创建的是同一个实例");
}else{
System.out.println("创建的不是同一个实例");
}
}
}
运行结果:
结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。
转:http://blog.csdn.net/jason0539/article/details/23297037
http://www.cnblogs.com/java-my-life/archive/2012/03/31/2425631.html