单例在实际的项目中个使用频率比较高,比如环境类,工具类,管理器类,都应该设计成单例,即保证项目运行中该类对象只有一个类对象存在,单例按照对象初始化时间,分为饿汉式和懒汉式,饿汉式和懒汉式分别代表了软件开发中对象或者数据初始化的两种方式,饿汉式的意思是在不管什么时候使用,程序一启动就开始初始化操作,懒汉式的意思是初始化的时间延后至程序要使用的时候才进行:
饿汉式的方式相对简单:
package gionee.studydemo; /** * Created by smarts on 15-11-12. */ public class SingletonTest { private static SingletonTest ourInstance = new SingletonTest(); public static SingletonTest getInstance() { return ourInstance; } private SingletonTest() { } } 这段代码是android studio的单列功能菜单自动生成的,可见谷歌是建议用这种简单的方式稍有些资源浪费的方式,这种方式对多线程也是安全的。懒汉式的写法避免了资源浪费的问题,但是稍显复杂,也分为两种:
第一种写法:
class SingleTon{
private SingleTon() { (1)
}
public static volatile SingleTon sInstance; (2)
public static SingleTon getInstance() {
if (sInstance == null) {
synchronized (SingleTon.class) { (3)
if (sInstance == null) { (4)
sInstance = new SingleTon();
}
}
}
return sInstance;
}
}
这种写法就是定义一个静态的成员变量,只要检查到这个变量为空的话就对这个变量做初始化操作;这里对标注的几个地方做下解释:
(1) 私有化构造函数,这样确保了在类的外部不可能实例化这个类,保证获取类对象的接口只能通过getInstance();
(2) 起关键作用的静态成员变量,值得关注的是volatile修饰符,关于这个修饰符的详细原理就不在这里阐述;需要明白的是public static成员变量的值可以被多线程同时访问,线程在修改这个变量的时候,会先把修改的值缓存在一个属于线程的内存空间,之后再把它写到公共的内存空间里去,也就是说这个变量可能存在多份不一样值的拷贝,这里可能造成的问题就是一个线程初始化了sInstance的值,但是还没写到公共空间,而另一个线程读取了公共空间的值,判断为空,又去初始化一次实例。而volatile的作用恰恰就是禁止这种多分拷贝的存在,使得每个线程读写的都是同样的一块内存,这样就不会存在初始化多个实例的情况;
(3) 只有判断sInstance等于空后才同步代码块,因为同步代码对资源消耗很大,所以synchronized关键字不能滥用;
(4) 当获取同步锁进入后,还要再一次判断是否为空,原因是可能是释放同步锁的线程实例化过对象,此时变量已经不是空;
第二种写法:
class SingalTon{
private SingleTon() {
}
private static class SingleTonHolder {
private static SingleTon sInstance = new SingleTon();
}
public static SingleTon getInstance() {
return SingleTonHolder.sInstance;
}
}
这种写法巧妙的运用了静态内部类,静态内部类只有在调用的时候才会进行类加载,classLoader加载一个类只会加载一次,这样就是借助虚拟机实现了单例的目的,并且不用自己去控制同步;相比上一种简单、明了,所以我很推荐用这种方法,Effective也推荐只用这种方法