设计模式(一):单例模式
一.单例模式的特点:
在单例模式的开始和结束中,这个过程只会实例化一个对象。
二.懒汉式的写法及特点:
懒汉式顾名思义就是越懒越好,你不用我,我便不去实例化。只有在调用时,才会进行实例化操作。
在开始到结束的生命周期中只进行一次实例化。
1 package com.cllover.lazy; 2 3 public class LazySingleton { 4 public static void main(String[] args) { 5 6 lazyinstance instance = lazyinstance.getInstance(); 7 System.out.println(instance+"\n"); 8 lazyinstance instance1 = lazyinstance.getInstance(); 9 System.out.println(instance1+"\n"); 10 11 12 } 13 } 14 15 16 class lazyinstance{ 17 18 private static lazyinstance instance; 19 20 private lazyinstance(){ 21 } 22 23 public static lazyinstance getInstance() { 24 if (instance == null){ 25 instance = new lazyinstance(); 26 } 27 return instance; 28 } 29 }
在单线程下,由于第一次进行实例化在此前并没有提前进行实例化操作,所以符合条件进行实例化操作
在实例化化得过程中进行了new空间 ,初始化和引用赋值等几个步骤。再进行第二次实例化时,由于第一次已经存在值,所以第二次条件不成立,直接返回第一次实例化结果,所以两次结果相等为true
三.那么在多线程下是否安全那?
1 package com.cllover.lazy; 2 3 public class LazySingleton { 4 5 public static void main(String[] args) { 6 //线程一 7 new Thread(()->{ 8 lazyinstance instance = lazyinstance.getInstance(); 9 System.out.println(instance+"\n"); 10 }).start(); 11 //线程二 12 new Thread(()->{ 13 lazyinstance instance1 = lazyinstance.getInstance(); 14 System.out.println(instance1+"\n"); 15 }).start(); 16 } 17 } 18 class lazyinstance{ 19 20 private static lazyinstance instance; 21 22 private lazyinstance(){ 23 } 24 25 public static lazyinstance getInstance() { 26 27 if (instance == null){ 28 instance = new lazyinstance(); 29 } 30 return instance; 31 } 32 }
多线程下的懒汉式
很显然在多线程下,各自线程进行自己的实例化操作, 造成不安全的因素在里面,在进行相同的实例化操作下 并不相同,
与此同时,对实例化进行加lock或者加 synchronized 进行加锁。
四.使用synchronized 同步锁
synchronized能够解决在多线程的条件下,控制线程无法同时进行,进行同步堵塞。在这里定义了三个线程进行验证
1 package com.cllover.lazy; 2 3 public class LazySingleton { 4 5 public static void main(String[] args) { 6 //线程一 7 new Thread(()->{ 8 lazyinstance instance = lazyinstance.getInstance(); 9 System.out.println(instance+"\n"); 10 }).start(); 11 //线程二 12 new Thread(()->{ 13 lazyinstance instance1 = lazyinstance.getInstance(); 14 System.out.println(instance1+"\n"); 15 }).start(); 16 //线程三 17 new Thread(()->{ 18 lazyinstance instance2 = lazyinstance.getInstance(); 19 System.out.println(instance2+"\n"); 20 }).start(); 21 } 22 } 23 class lazyinstance{ 24 25 private static lazyinstance instance; 26 27 private lazyinstance(){ 28 } 29 30 public static lazyinstance getInstance() { 31 32 if (instance == null){ 33 //同步堵塞 34 synchronized (lazyinstance.class){ 35 //是否进行过实例化 36 if (instance == null){ 37 instance = new lazyinstance(); 38 } 39 } 40 } 41 return instance; 42 } 43 }
在进行了加锁之后,结果相同。
原因:
在三个线程同时运行中,由于synchronized的作用并没有同时进行实例化操作。
先有“线程一”就行实例化,由于是第一次实例化所以条件符合,new了一个新的实例。第一个线程结束后,第二个线程由“线程三”开始进行检测是否已经存在instance的值。由于“线程一”的实例化,进行了引用赋值,所以在“线程二”中可以直接获取值。并在检测中显示不符合条件,存在这个值。并带回这个结果。
最后进行“线程一”由于存在值,所以不用new新的实例,并带回结果。所以再加入锁的情况下懒汉式多线程相对较为安全。
五.饿汉式Singleton:
在每次开始时便自动进行实例化,不需要调用便实例化成功。
使用static 在类加载时便进行了实例化。;在多线程下相对更安全。
1 package com.cllover.hunger; 2 /* 3 * 饿汉式单例模式 4 * */ 5 public class hungerSingleton { 6 public static void main(String[] args) { 7 8 new Thread(()->{ 9 hungerInstance instance = hungerInstance.getInstance(); 10 System.out.println(instance); 11 }).start(); 12 new Thread(()->{ 13 hungerInstance instance1 = hungerInstance.getInstance(); 14 System.out.println(instance1); 15 }).start(); 16 } 17 } 18 19 class hungerInstance{ 20 21 private static hungerInstance instance = new hungerInstance(); 22 private hungerInstance(){ 23 } 24 25 public static hungerInstance getInstance() { 26 return instance; 27 } 28 }
在线程开始前进行实例化并设置有初值,两个线程同时开始后获取到相同的值