单例模式就是是一个类仅可以创建一个对象,在java中实现主要有两种方式:饿汉式和懒汉式。
先看两种方式共有的部分,因为是单例,所以构造方法必须是私有的private,而且必须提供一个对外界开放的获取对象的方法,该方法内部控制返回唯一的一个对象实例:
public class Singleton { //构造方法私有,阻断外界直接创建对象的方法 private Singleton() {} //提供一个获取唯一对象实例的方法 public static Singleton getInstance(){ //... } }
以上是不管什么方式的实现,都得遵循的一些规定,下面就介绍懒汉式和饿汉式:
饿汉式是单例类感觉自己很饥饿(将实例对象想象为吃的),不管有没有别的类跟我要实例类,我都要自己先生成一个,像下面的实现:
public class Singleton { //构造方法私有,阻断外界直接创建对象的方法 private Singleton() {} //内部持有一个类初始化时唯一创建的一个类实例对象 private static Singleton singleton = new Singleton(); //提供一个获取唯一对象实例的方法 public static Singleton getInstance(){ return singleton; } }
饿汉式的有点在于编码逻辑简单好理解,无线程安全问题;缺点嘛,自然就是当我仅仅是想用Singleton类其他方法,他还是创建了一个对我现在来说没用的实例对象,当系统中这种饿汉式的单例类多起来的时候,无疑是一种资源浪费,这个问题正好懒汉式可以解决。
懒汉式,就是自己首先持有一个空的单例类的实例,但是不会类一加载就创建实例,只有当别人第一次要我的实例对象我才给他创建,懒嘛,要是没人要我就不创建了:
public class Singleton { //构造方法私有,阻断外界直接创建对象的方法 private Singleton() {} //内部持有一个单例类的引用 private static Singleton singleton = null; //提供一个获取唯一对象实例的方法 public static Singleton getInstance(){ if(singleton==null){ singleton = new Singleton(); } return singleton; } }
饿汉式的有点不用说了,就是解决了懒汉式的缺点;但是他的缺点就是多线程环境下,不尽人意啊,比如两个线程同时都是第一次去获取Singleton类的实例的时候,又同时执行到if(singleton==null)这一行,两个线程就会同时进入if语句执行体中去,可能先后执行了singleton = new Singleton();这就违反了单例模式的概念,所以还得想个办法解决这个问题。
线程安全的饿汉式:(其实只要将if语句上加上线程锁,就可以避免两个线程一起跑到这个地方来了)
public class Singleton { //构造方法私有,阻断外界直接创建对象的方法 private Singleton() {} //内部持有一个单例类的引用 private static Singleton singleton = null; //提供一个获取唯一对象实例的方法 public static Singleton getInstance(){ synchronized (Singleton.class) { if(singleton==null){ singleton = new Singleton(); } } return singleton; } }
踏哒~,解决了,但是!你发现了么?这样的话,每次别人想获取sinleton实例的时候都得等待别的线程释放锁,自己再加锁,自己再释放锁,而这些锁的操作又是那么消耗时间,能不能再优化一下。想一想,是不是第一次访问完了,只要sington对象实例,不为空,直接返回就是了,没有现成问题啊,应该这样:
public class Singleton { //构造方法私有,阻断外界直接创建对象的方法 private Singleton() {} //内部持有一个单例类的引用 private static Singleton singleton = null; //提供一个获取唯一对象实例的方法 public static Singleton getInstance(){ if(singleton==null){ synchronized (Singleton.class) { if(singleton==null){ singleton = new Singleton(); } } } return singleton; } }
完美!线程安全的懒汉式,既解决了资源浪费问题,又兼顾了线程安全问题。
不过还有个更巧妙地方法,这个就涉及到内部类的只是,当一个类加载的时候,其内部类并不加载,而是只要第一次用到内部类的时候内部类才会加载,而且如果这个内部类是static内部类,也就是说加载了这个类一次,以后就直接获取他就可以了,详细参见另一篇博客:http://www.cnblogs.com/WreckBear/p/5812942.html。
public class Singleton { //构造方法私有,阻断外界直接创建对象的方法 private Singleton() {} /* * 搭建一个内部静态类,外部类加载的时候,内部类并不会加载, * 只有当内部类被访问的时候才会被加载、初始化,加载之后就会一直保存在内存中 */ public static class Get{ public static Singleton singleton = new Singleton(); } }
在Main中获取:
public class Main { public static void main(String[] args) { Singleton singleton = Singleton.Get.singleton; } }
这种方法显得更加优雅一点,至此,结束!