单例模式,androidstudio简易app实例

/**

  • 静态私有的成员变量持有Singleton对象的引用

*/

private static Singleton singleton;

/**

  • 私有构造函数,只有类的内部可以使用

*/

private Singleton() {

}

/**

  • 静态方法获取实例对象

*/

public static Singleton getInstance() {

if (singleton == null) {

singleton = new Singleton();

}

return singleton;

}

}

public void doSomething() {

}

public class Client {

public static void main(String[] args) {

Singleton instance = Singleton.getInstance();

instance.doSomething();

}

}

单例模式中的线程安全问题

在高并发多线程访问单例的情况下,上述代码是不安全的。为什么?假设第一个线程执行到getInstance()中的singleton = new Singleton()时,第二个线程也开始访问getInstance()方法,但是此时第一个线程要获取的实例对象还没完成,也就是说new Singleton()需要一定的时间,那么此时第二个线程访问判断singleton == null的条件还是成立的,那就会进行创建对象的过程。因此这时会出现创建了两个对象,两个线程访问的不是同一个对象,假如有若干线程同时访问,则产生对象的数量不唯一。

解决单例线程安全的方法:

1. 将访问方法变为同步方法(synchronized)

public class Singleton {

private static Singleton singleton;

private Singleton() {}

public static synchronized Singleton getInstance() {

if (singleton == null) {

singleton = new Singleton();

}

retu单例模式,androidstudio简易app实例_第1张图片
rn singleton;

}

}

使用synchronized关键字可以避免多线程访问的安全问题,这是java语言的特性,synchronized使得一个线程在访问此方法时必须等待另一个线程离开该方法,相当于给方法加锁,但是这样也不是最佳的方式,因为一旦加了synchronized关键字就意味着在每次访问时都要进行同步操作,而同步是影响性能的,假如同步造成的性能影响对系统来说可以忽略,那么你可以不必在意。

2.使用饿汉单例模式

上述代码所示例的单例模式又称为懒汉单例模式,与之对应的还有一种就是饿汉单例模式,懒汉是将对象初始化延时处理,而饿汉则是在定义成员变量的时候直接初始化对象:

public class Singleton {

private static Singleton singleton = new Singleton();

private Singleton() {}

public static Singleton getInstance() {

return singleton;

}

}

这种方式是直接静态初始化一个实例对象,Java虚拟机JVM在加载类的时候就会创建该实例,这样就能保证在任何线程访问的时候该实例一定存在且唯一了。因为单例模式的目的就是确保存在一个唯一对象, 如果直接静态初始化带来的负担不是很大,可以考虑这种方式。但是如果new Singleton()需要的代价比较大,比如会占用极高的内存或者比较耗时,那么你需要慎重考虑了,因为假如实例对象并没有机会被访问到(假设你只用到了其中的静态方法),那么直接静态初始化了一个对象是不是浪费了资源呢。

饿汉和懒汉相比,还有一种极端的情况就是,假如对象长期未被使用,JVM是有可能回收掉该对象的,一旦被回收掉,懒汉模式下getInstance()会重新创建该对象,而饿汉模式下getInstance()则会直接返回null,但是这种情况现在基本不会存在,在java1.2以前会存在这个问题,后续的jdk版本中java修复了这个bug。

3.使用双重检查加锁减少同步次数(Double Check Lock)

为了减少同步的次数,我们可以将synchronized移到方法的内部,因为实际上一旦我们创建好了实例对象,实例对象已经存在的情况下,其它线程再来访问肯定是同一个对象。所以只需要在第一次访问的时候提供同步加锁就可以,第二次对象已存在就不需要再加锁了。

public class Singleton {

/*使用volatile保证对该变量的写入对另一个线程可见/

private volatile static Singleton mSingleton = null;

private Singleton(){}

public static Singleton getInstance() {

if (mSingleton == null) {

synchronized (Singleton.class) {

if (mSingleton == null) {

mSingleton = new Singleton();
te Singleton(){}

public static Singleton getInstance() {

if (mSingleton == null) {

synchronized (Singleton.class) {

if (mSingleton == null) {

mSingleton = new Singleton();

你可能感兴趣的:(程序员,架构,移动开发,android)