懒汉式单例模式的代码实现及其使用代码展示为什么需要进行二次判空

一、首先看下简单的懒汉式单例模式的代码实现:

public class Singleton {
      
     private static Singleton ss;
     
     private Singleton(){};
     
     public static Singleton getSingleton(){         
          if (ss == null) {
              synchronized (Singleton.class) { 
                   if (ss == null) {                   
                        ss = new Singleton();
                        i++;
                        System.out.println(i);
                   }                                     
              }    
          }    
          return ss;
     }
}

二、接下来,使用代码来解释下为什么需要进行二次判空

public class Singleton {
	
	//使用volatile禁止指令重排序
	private static volatile Singleton sin = null;
	
	private static int i = 0;//标识有几个线程获取到了锁
	private static int j = 0;//标识系统中到底生成了几个实例
	
	//将构造器的修饰符设置为"private",可以防止在外部进行new实例对象
	private Singleton(){};
	
	//获取实例对象的方法,公共的方法。
	public static Singleton getInstance(){
		//第一次判空。
		if (sin == null) {
			//获取"类对象"的锁
			synchronized (Singleton.class) {
				i++;
				//第二次判空。
				if (sin == null) {
					sin = new Singleton();
					j++;
				}
			}
		}
		return sin;
	}
	
	
	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			Thread thread = new Thread(new Runnable() {
				
				@Override
				public void run() {
					try {
						//打印出线程名称
						System.out.println(Thread.currentThread().getName());
						//让一个获取锁的线程睡眠,使其他的线程能够进入到"竟锁池"中等待获取锁
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					//获取实例
					Singleton.getInstance();
				}
			}, "thread_"+i);
			
			thread.start();
			
		}
		
		while(true){
			//判断当前活跃线程数是否为1,且只为 main线程
			if (Thread.activeCount() == 1) {
				//打印出唯一活跃线程的名称
				System.out.println("\n" + Thread.currentThread().getName() + " 线程");
				//开启的10个线程中几个线程获取到了锁
				System.out.println("共有 ( "+ i + " ) 个线程获取到对象锁");
				//最终生成了几个Singleton实例
				System.out.println("最终生成了( "+j + " )个Singleton实例对象");
				break;
			}
		}

	}
}

         运行上面的mian方法,会得到以下的一种输出结果(存在多种输出结果):

thread_0
thread_2
thread_1
thread_3
thread_4
thread_5
thread_6
thread_7
thread_9
thread_8

main 线程
共有 ( 3 ) 个线程获取到对象锁
最终生成了( 1 )个Singleton实例对象

        从运行结果可以看出,如果不进行第二次判空的话,那么在竟锁池(锁池)中如果还有活跃的线程在等待获取的锁的话,在锁释放后就会再次竞争获取锁,获取的锁的线程进入"就绪状态",当cpu分配其"时间片"后进行线程的调度,从而线程进入"运行中状态",并会去执行同步的代码块,如果在没加如二次判空的话,就会导致系统中存在多个实例,而在进行判空后,即使你获取到了锁,但在执行同步代码块时也会直接跳过。

       竟锁池(锁池)的概念:参考此作者的解读  https://blog.csdn.net/qq_22498277/article/details/82184419

发布了20 篇原创文章 · 获赞 31 · 访问量 9580

猜你喜欢

转载自blog.csdn.net/feichitianxia/article/details/89643271