开发者

Java设计模式之单例模式Singleton Pattern详解

开发者 https://www.devze.com 2023-12-05 10:41 出处:网络 作者: 制造bug的键盘钢琴师
目录1.单例设计模式的设计思想与应用场景2.单例模式的写法2.1单例模式的饿汉式2.2单例模式的懒汉式2.2.1 懒汉式普通写法2.2.2懒汉式同步代码写法2.2.3懒汉式同步方法写法2.2.4单例模式懒汉式双重校验锁2.3内部类2.4枚
目录
  • 1.单例设计模式的设计思想与应用场景
  • 2.单例模式的写法
    • 2.1单例模式的饿汉式
    • 2.2单例模式的懒汉式
      • 2.2.1 懒汉式普通写法
      • 2.2.2懒汉式同步代码写法
      • 2.2.3懒汉式同步方法写法
      • 2.2.4单例模式懒汉式双重校验锁
    • 2.3内部类
      • 2.4枚举
      • 总结

        1.单例设计模式的设计思想与应用场景

        设计目的

        避免因为创建了多个实例造成资源的浪费,且多个实例由于多次调用容易导致结果出现错误,而使用单例模式能够保证整个应用中有且只有一个实例。

        设计思想

        (1)不允许其他程序用new对象: 因为new就是开辟新的空间,在这里更改数据只是更改的所创建的对象的数据,如果可以new的话,每一次new都产生一个对象,这样肯定保证不了对象的唯一性。

        (2)在该类中创建对象: 因为不允许其他程序new对象,所以这里的对象需要在本类中new出来

        (3)对外提供一个可以让其他程序获取该对象的方法

        应用场景

        一些常用的工具类、线程池、缓存,数据库,数据库连接池、账户登录系统、配置文件等程序中可能只允许我们创建一个对象。

        2.单例模式的写法

        单例模式的写法大的方面可以分为5种五种

        1.懒汉式

        2.饿汉式

        3.双重校验锁

        4.静态内部类

        5.枚举

        2.1单例模式的饿汉式

        public class Singleton {
        		private static Singleton instance=new Singleton();
        		//构造器私有化,本身不可创建
        		private Singleton(){
        		}
        		//定义一个获取对象的方法
        		public static Singleton getInstance(){
        			 return instance;
        		}
        }
        

        饿汉式的静态代码块写法

        public class Singleton{
        	private static Singleton  instance = null;
        	static{
        		 instance =  new Singleton();
        	}
        	private Singleton(){
        	}
        	public static  Singleton getInstance(){
        		return instance;
        	}
        }
        

        备注:两种写法的主要区别在于加载的时间点上,前者在于静态常量显示初始化之后,后者在与静态代码块加载运行的时候。

        优点:饿汉js式的写法主要的优势在于实现方式简单,在类加载的时候完成了对象的实例化,因此也可以有效避免线程问题。

        缺点:可能造成资源浪费的情况,由于对象实例是在代码加载过程中进行的,只要代码成功加载对象实例就会被创建导致内存浪费。【被占用的内存是非常小的,所以也是推介可以使用的。】

        2.2单例模式的懒汉式

        2.2.1 懒汉式普通写法

        线程不安全,不推荐使用

        public class Singleton {
         
        	private static Singleton instance=null;
        	
        	private Singleton() {};
        	
        	public static Singleton getInstance(){
        		
        		if(instance==null){
        			instance=new Singleton();
        		}
        		return instance;
        	}
        }
        

        与饿汉式的写法相比较,懒汉式主要是在对**getInstance(android)**方法调用的时候进行实例化对象的操作,既用之再造,节省了内存,故因此称之为懒汉式。

        懒汉式的普通写法式存在线程安全问题的,总的来说也就是存在重复创建对象的风险。假设线程1进来,通过if(instance==null)的条件判断准备运行”insatance=new Singleton()“这条代码前,又有线程2运行,此时还没有成功进行Singleton对象实例的创建,if 的条件判断仍然成立,线程2也会去执行instance=new Singleton()这条语句,这样就会导致多个Singleton对象被创建。这就是为什么懒汉式的普通写法会造成线程安全的原因。

        2.2.2懒汉式同步代码写法

        【给创建对象的语句加锁】与2.2.1的写法类似仍为线程不安全的写法,不推荐使用

        public class Singleton7 {
         
        	private static Singleton instance=null;
        	
        	public static Singleton getInstance() {
        		if (instance == null) {
        			synchronized (Singleton.class) {
        				instance = new Singleton();
        			}
        		}
        		return instancwww.devze.come;
        	}
        }
        

        与2.2.1相类似,synchronized (Singleton.class) 锁住的只是对于对象的创建,仍有有可能再线程1获得”钥匙“还未进行对象的创建的时候,线程二也通过条件判断进行锁等待,一旦获得“钥匙”也会进行对象创建,也是会导致多对象的创建。

        2.2.3懒汉式同步方法写法

        线程安全,但效率非常低,不推荐使用

        public class Singleton {
         
        	private static Singleton instance=null;
        	
        	private Singleton() {};
        	
        	public static synchronized Singleton getInstance(){
        		
        		if(instance==null){
        			instance=new Singleton();
        		}
        		return instance;
        	}
        }
        
        

        缺点:效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步,方法进行同步效率太低。

        2.2.4单例模式懒汉式双重校验锁

        推介使用

        public class Singleton {
        	/**
        	 * 懒汉式变种,属于懒汉式中最好的写法,保证了:延迟加载和线程安全
        	 */
        	private static Singleton instance=null;
        	private Singleton() {};
        	public static Singleton getInstance(){
        		 if (instance == null) {  
        	          synchronized (Singleton.class) {  
        	              if (instance == null) {  
        	            	  instance = new Singleton();  
        	              }  
        	          }  
        	      }  
        	      return instance;  
        	}
        }

        通过双层if(insatance==null)的条件限制,防止了多线程运行就算在解锁条件下多对象的创建。

        通过synchroni编程客栈zed 保证了线程安全,但再第一次对象成功创建后,就可以通过return instance 避免了加锁代码的重复运行,大大提升了效率。

        在线程安全以及效率之间找到了一个平衡点。

        2.3内部类

        public class Singleton{
        	private Singleton() {};
        	private static class SingletonHolder{
        		private static Singleton instance=new Singleton();
        	} 
        	public static Singleton getInstance(){
        		return SingletonHolder.instance;
        	}
        }

        这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同

        的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时

        并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonHolder类,从而完成Singleton的实例化。

        类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是

        无法进入的。

        优点:避免了线程不安全,延迟加载,效率高。

        2.4枚举

        public enum SingletonEnum {
        	 instance; 
        	 private SingletonEnum() {}
        	 public void method(){
        	 }
        }

        也就是直接将枚举类Enum直接当作一个上述例子中的Singleton所对应的实例去进行使用,可以直接通过SingletonEnum.instance.method();的方式对实例中我们要使用的方法进行调用。

        不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。可能是因为枚举在JDK1.5中才添加,所以在实际项目开发中,很少见人这么写过,这种方式也是最好的一种方式,如果在开发中JDK满足要求的情况下建议使用这种方式。

        总结

        对于写法的选择,开源项目也是采用的单例模式懒汉式双重校验锁这种写法,其实最安全的写法是枚举,它的实现非常简单而且最安全可谓很完美,但是可能是因为支持的JDK版本有限又或者是因为枚举大家不熟悉所以目前使用的人并不多,可以更多的进行尝试。

        另外当我们使用反射机制时可能不能保证实例的唯一性,但是枚举始终可以保证唯一性【这与枚举类在初始类加载的过程以及进行的操作有关】。

        到此这篇关于Java设计模式之单例模式Singleton Pattern详解的文章就介绍到这了,更多相关Java单例模式Singleton Pattern内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多js支持编程客栈(www.devze.com)!

        0

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        关注公众号