单例模式--笔记
单例模式是一种对象创建型模式,使用单例模式,可以保证一个类只能唯一的生成一个实例化对象
GoF对单例模式的定义:保证在一个类中,只有一个实例存在,同时提供能对该实例加以访问的全局访问方法。
在应用开发系统中,我们常常需要以下需求:
1).在多线程之间,如servlet环境,共享同一个资源或者操作同一个对象
2).在整个程序空间启用全局变量,共享资源
3).大规模系统中,为了性能的考虑,需要节省对象的创建时间等等
因为Singleton模式可以保证为一个类只生成唯一的一个实例对象,所以对于上述情况,Singleton模式就派上用场了。
单例模式实现:
1.饿汉式;
2.懒汉式;
3.双重检查
一般实现:
1 public class Person{ 2 3 priate String name; 4 5 public String getName(){ 6 7 return name; 8 } 9 public String setName(){ 10 11 return this.name=name; 12 } 13 14 15 }
主类:
1 public class MainClass{ 2 public static void main(String[] args){ 3 4 Person per=new Person(); 5 Person per2=new Person(); 6 //定义变量 7 per.setName("张三"); 8 per2.setName(“李四”); 9 //通过person类生成了两个对象 10 System.out.println(per.getName()); 11 System.out.println(per2.getName()); 12 13 } 14 }
此时,在非单例模式下,定义两个对象,得到了输出结果为:
张三
李四
首先展示的是饿汉式:
1 public class Person{ 2 3 priate String name; 4 public static final Person person =new person();//饿汉式设置 5 // 6 public String getName(){ 7 8 return name; 9 } 10 public String setName(){ 11 12 return this.name=name; 13 } 14 //构造函数私有化 15 private person(){ 16 17 } 18 //构造一个全局的静态方法:因为如果不是全局的,那么需要通过对象来进行调用,如果定义成全局的静态,就可以通过类来调用 19 public static Person getPerson(){ 20 return new Person(); 21 } 22 }
此时,对于主类修改为:
1 public class MainClass{ 2 public static void main(String[] args){ 3 Person per=Person.getPerson();//不再使用new来定义对象,用类定义 4 Person per2=Person.getPerson(); 5 //定义变量 6 per.setName("张三"); 7 per2.setName("李四"); 8 //通过person类生成了两个对象 9 System.out.println(per.getName()); 10 System.out.println(per2.getName()); 11 12 } 13 }
得到的结果为:
李四
李四
懒汉式:
1 public class Person{ 2 3 priate String name; 4 private static Person person;//静态初始化默认为null 5 public String getName(){ 6 return name; 7 } 8 public String setName(){ 9 10 return this.name=name; 11 } 12 //构造函数私有化 13 private person(){ 14 } 15 public static Person getPerson(){ 16 //此处添加一个判断 17 if(person==null) person=new Person; 18 return person(); 19 } 20 }
结果为:
李四
李四
上述两种情形各有所长,在饿汉式中,一加载Person类就进行初始化对象,保证同一个对象
在上述懒汉式中,可以保证单线程正常单例模式
允许多线程的懒汉式单例模式:
1 public class Person{ 2 3 priate String name; 4 private static Person person;//静态初始化默认为null 5 public String getName(){ 6 return name; 7 } 8 public String setName(){ 9 10 return this.name=name; 11 } 12 //构造函数私有化 13 private person(){ 14 } 15 //使用一个全局的静态方法,使用同步方式 16 public static Synchrogazed Person getPerson(){ 17 //当有一个线程进来时,这个方法就被霸占了,对于进来的方法就是将执行完毕在允许其他线程进入 18 if(person==null) //第一步 19 person=new Person; // 第二步 20 return person();//第三步 21 } 22 }
双重检查:
原因:在上述的多线程代码段中,我们实现的是将整个方法都实现同步,
经过思考,我们可以发现,对于只有第一次的时候才会执行if内的内容;整个方法锁死后,接下来第二次,第三次。。。进来的都要进行判断if语句,影响执行效率
1 public class Person{ 2 3 priate String name; 4 private static Person person;//静态初始化默认为null 5 public String getName(){ 6 return name; 7 } 8 public String setName(){ 9 10 return this.name=name; 11 } 12 //构造函数私有化 13 private person(){ 14 } 15 //使用一个全局的静态方法 16 public static Person getPerson(){ 17 //当有一个线程进来时,这个方法就被霸占了,对于进来的方法就是将执行完毕在允许其他线程进入 18 if(person==null) { //第一步 19 Synchrogazed(Person.class){//仅仅同步化这一部分 20 if(person==null)//这个判断语句添加的原因 当有两个线程进来时,第一个线程和第二个线程都执行完
// if(person==null)这条语句,第一个线程进入同步,第二个线程等待,当第一个线程执行完,
//return之后,第二个线程又会执行同步语句,及时第一个线程已经new了一个对象,所以为防止这个情况的发生,
//需要在同步里边再判断一次,也就是第二次判断 21 person=new Person();} 22 } // 第二步 23 return person();//第三步 24 } 25 }