单例模式是最常用的设计模式之一,目的是保证一个类只有一个实例。
在项目中的作用:
1、解决因为频繁创建对象,导致资源消耗过大的问题,如:数据库的连接池,连接池用于创建数据库连接,并对连接进行回收使用,能减少数据库连接的创建次数,从而提高效率,但是连接池对象本身在项目中只需要一个,就需要使用单例模式。类似的还有线程池等。
2、项目中能共享的工具类,如Java中的Runtime类能提供各种运行环境系统参数,它就被设计成了单例模式。
实现单例的过程:
1、要保证类只能创建一个对象,就必须隐藏类的构造方法,所以要将构造方法定义为私有的
2、在类中调用构造方法创建对象,定义静态方法用于返回对象。
下面这种单例模式属于饿汉式单例模式,既一开始就将对象实例化。这样的做法会导致性能的降低,一般我们会采用延迟加载的方式,既需要对象时再实例化。
classHunger{ //静态的实例 privatestaticHungerhunger=newHunger(); //隐藏构造方法 privateHunger(){} //返回静态实例 publicstaticHungergetInstance(){ returnhunger; } }
下面这种是懒汉式单例模式,既开始不实例化对象,到需要该实例时再实例化,提高了运行效率。
publicclassLazySingleton{ //静态的实例 privatestaticLazySingletonsingle=null; //隐藏构造方法 privateLazySingleton(){ System.out.println("创建LazySingleton对象"); } //返回静态实例 publicstaticLazySingletongetInstance(){ if(single==null){ single=newLazySingleton(); } returnsingle; } }
线程安全问题:懒汉单例模式在单线程环境没有问题,但在多线程环境下就会出现问题。
执行代码,"创建LazySingleton对象"这句话会输出多次,也就是创建了多个对象。
for(inti=0;i<100;i++){ newThread(newRunnable(){ @Override publicvoidrun(){ System.out.println(LazySingleton.getInstance()); }}).start(); }
分析原因:
假设线程1满足getInstance方法中single==null条件后,准备执行创建对象的代码,然后CPU被其它线程抢占,其它线程在getInstance方法中创建对象,然后线程1抢回CPU继续执行刚才未完成的创建对象代码,这样就创建了多个对象。
线程同步问题的解决方法:
1、使用同步方法
publicstaticsynchronizedLazySingletongetInstance(){ if(single==null){ single=newLazySingleton(); } returnsingle; }
执行刚才多线程的代码后,我们发现可以解决同步问题,但是同步方法存在的问题是每个线程进入后都会加锁,执行效率低。
2、使用同步代码块配合if使用
publicstaticLazySingletongetInstance(){ if(single==null){ synchronized(LazySingleton.class){ single=newLazySingleton(); } } returnsingle; }
同步块和if语句配合使用,解决了每次都执行上锁导致的性能问题,但是运行代码后我们会发现,多线程同步的问题还是可能出现,原因是多个线程还是可能会同时进入if语句。
3、使用双重判断
在同步块中再添加一次if判断就解决了上面的问题,因为就算多个线程同时进入外层if语句,执行同步块还是要进行一次判断,这样第一个线程创建对象后,后面的线程就不能再创建了。
publicstaticLazySingletongetInstance(){ //外层的if主要作用是判断是否需要执行同步块,提高性能 if(single==null){ //静态方法中将类作为锁 synchronized(LazySingleton.class){ //判断对象是否为空,为空就创建对象 if(single==null){ single=newLazySingleton(); } } } returnsingle; }
4、静态内部类
这种方法结合了饿汉式和懒汉式的特点,如果不调用getInstance方法,静态内部类中的创建对象代码不会执行,调用getInstance后才会执行,也就有了懒汉式延迟加载的效果,并且由于对象是直接创建的,还不存在线程同步问题。
classMySingleton{ privateMySingleton(){ } privatestaticclassSingletonHelp{ staticMySingletoninstance=newMySingleton(); } publicstaticMySingletongetInstance(){ returnSingletonHelp.instance; } }
总结:单例模式的实现一般有饿汉式、懒汉式和静态内部类等方式,饿汉式创建对象的性能比较低但不存在线程同步问题,懒汉式由于是延迟加载性能更高,但存在线程同步问题,需要使用双重判断解决,静态内部类的方式也能实现单例模式但是代码可读性稍差。
如果项目对性能不敏感推荐使用饿汉式,如果是单线程环境可以使用一般的懒汉式,如果需要多线程则可以使用多重判断的懒汉式或静态内部类实现。
以上就是关于Java开发单例设计模式线程同步的详细介绍,最后想要了解更多可以登录扣丁学堂官网咨询。扣丁学堂是专业的Java培训机构,不仅有专业的老师和与时俱进的课程体系,还有大量的Java视频教程供学员观看学习,想要学好JavaEE的小伙伴抓紧时间行动吧。扣丁学堂java技术交流群:487098661。微信号:codingbb