码迷,mamicode.com
首页 > 编程语言 > 详细

Java单例模式

时间:2017-04-09 12:55:05      阅读:126      评论:0      收藏:0      [点我收藏+]

标签:方法   乱序   读取   空间   let   单例   静态   gets   推荐   

一、饿汉式

 这种方式基于classloder机制避免了多线程的同步问题

1 public class Singleton {  
2     private static Singleton instance = new Singleton();  
3     private Singleton (){}  
4     public static Singleton getInstance() {  
5     return instance;  
6     }  
7 }

 这种实现方式适合单例占用内存比较小,在初始化时就会被用到的情况。但是如果单例占用的内存比较大,或单例只是在某个特定场景下才会用到,使用饿汉模式就不合适了,这时候就需要用到懒汉模式进行延迟加载。

 

二、懒汉式单例

 

使用静态内部类

这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance

并且由于静态内部类只会被加载一次,所以这种写法也是线程安全的:

1 public class Singleton {  
2     private static class SingletonHolder {  
3     private static final Singleton INSTANCE = new Singleton();  
4     }  
5     private Singleton (){}  
6     public static final Singleton getInstance() {  
7     return SingletonHolder.INSTANCE;  
8     }  
9 }

 

双重检查锁定

在加锁前多进行一次null检查就可以减少绝大多数的加锁操作,执行效率提高的目的也就达到了。

如果不使用volatile,由于指令重排优化的存在,导致初始化Singleton和将对象地址赋给singleton字段的顺序是不确定的。在某个线程创建单例对象时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给singleton字段了,然而该对象可能还没有初始化。若紧接着另外一个线程来调用getSingleton,取到的就是状态不正确的对象,程序就会出错。

 1 public class Singleton {  
 2     private volatile static Singleton singleton;  
 3     private Singleton (){}  
 4     public static Singleton getSingleton() {  
 5     if (singleton == null) {  
 6         synchronized (Singleton.class) {  
 7         if (singleton == null) {  
 8             singleton = new Singleton();  
 9         }  
10         }  
11     }  
12     return singleton;  
13     }  
14 }

volatile这个关键字就是可见性。可见性指的是在一个线程中对该变量的修改会马上由工作内存(Work Memory)写回主内存(Main Memory),所以会马上反应在其它线程的读取操作中。顺便一提,工作内存和主内存可以近似理解为实际电脑中的高速缓存和主存,工作内存是线程独享的,主存是线程共享的。

volatile的第二层语义是禁止指令重排序优化。大家知道我们写的代码(尤其是多线程代码),由于编译器优化,在实际执行的时候可能与我们编写的顺序不同。编译器只保证程序执行结果与源代码相同,却不保证实际指令的顺序与源代码相同。这在单线程看起来没什么问题,然而一旦引入多线程,这种乱序就可能导致严重问题。volatile关键字就可以从语义上解决这个问题。

 

使用枚举

使用枚举除了线程安全和防止反射强行调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。因此,Effective Java推荐尽可能地使用枚举来实现单例。

 1 public enum Singleton {
 2     INSTANCE;
 3     private String name;
 4     public String getName(){
 5         return name;
 6     }
 7     public void setName(String name){
 8         this.name = name;
 9     }
10 }

 

饿汉式与懒汉式区别

1、饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用单例时速度也会更快,因为其资源已经初始化完成。
2、懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

如果初始化工作比较多,占用资源少,调用次数多,建议使用饿汉式,在访问的时候可以加快调用的速度。

如果某个单例使用的次数少,并且创建单例消耗的资源较多,那么就需要实现单例的按需创建,这个时候使用懒汉模式就是一个不错的选择。

Java单例模式

标签:方法   乱序   读取   空间   let   单例   静态   gets   推荐   

原文地址:http://www.cnblogs.com/chn58/p/6684086.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!