只有光头才能变强html
回顾前面:java
原本打算没那么快更新的,这阵子在刷Spring的书籍。在看Spring的时候又常常会看到“单例”,“工厂”这些字样。程序员
因此,就先来讲说单例和工厂设计模式啦,这两种模式也是很常见的,我看不少面经都会遇到这两种模式~设计模式
本文主要讲解单例设计模式,若是有错的地方但愿能多多包涵,并不吝在评论区指正!安全
单例模式定义很简单:一个类中能建立一个实例,因此称之为单例!微信
那咱们何时会用到单例模式呢??多线程
学过Java Web的同窗可能就知道:函数
那既然多例是频繁建立对象、须要管理对象的,那Struts2为何要多例呢??post
能使用一个对象来作就不用实例化多个对象!这就能减小咱们空间和内存的开销~性能
那有可能有的人又会想了:咱们使用静态类.doSomething()
和使用单例对象调用方法的效果是同样的啊。
静态类.doSomething()
体现的是基于对象,而使用单例设计模式体现的是面向对象。编写单例模式的代码其实很简单,就分了三步:
根据上面的步骤,咱们就能够轻松完成建立单例对象了。
public class Java3y { // 1.将构造函数私有化,不能够经过new的方式来建立对象 private Java3y(){} // 2.在类的内部建立自行实例 private static Java3y java3y = new Java3y(); // 3.提供获取惟一实例的方法 public static Student getJava3y() { return java3y; } }
这种代码咱们称之为:“饿汉式”:
既然说一上来就建立对象,若是没有用过会形成内存浪费:
public class Java3y { // 1.将构造函数私有化,不能够经过new的方式来建立对象 private Java3y(){} // 2.1先不建立对象,等用到的时候再建立 private static Java3y java3y = null; // 2.1调用到这个方法了,证实是要被用到的了 public static Java3y getJava3y() { // 3. 若是这个对象引用为null,咱们就建立并返回出去 if (java3y == null) { java3y = new Java3y(); } return java3y; } }
上面的代码行不行??在单线程环境下是行的,在多线程环境下就不行了!
要解决也很简单,咱们只要加锁就行了:
上面那种直接在方法上加锁的方式其实不够好,由于在方法上加了内置锁在多线程环境下性能会比较低下,因此咱们能够将锁的范围缩小。
public class Java3y { private Java3y() { } private static Java3y java3y = null; public static Java3y getJava3y() { if (java3y == null) { // 将锁的范围缩小,提升性能 synchronized (Java3y.class) { java3y = new Java3y(); } } return java3y; } }
那上面的代码可行吗??不行,由于虽然加了锁,但仍是有可能建立出两个对象出来的:
getJava3y()
方法,他们同时判断java==null
,得出的结果都是为null,因此进入了if代码块了有的同窗可能以为我瞎吹比,明明加锁了还不行?咱们来测试一下:
public class TestDemo { public static void main(String[] args) { // 线程A new Thread(() -> { // 建立单例对象 Java3y java3y = Java3y.getJava3y(); System.out.println(java3y); }).start(); // 线程B new Thread(() -> { // 建立单例对象 Java3y java3y = Java3y.getJava3y(); System.out.println(java3y); }).start(); // 线程C new Thread(() -> { // 建立单例对象 Java3y java3y = Java3y.getJava3y(); System.out.println(java3y); }).start(); } }
能够看到,打印出的对象不仅仅只有一个的!
厉害的程序员又想到了:进入同步代码块时再判断一下对象是否存在就稳了吧!
public class Java3y { private Java3y() { } private static Java3y java3y = null; public static Java3y getJava3y() { if (java3y == null) { // 将锁的范围缩小,提升性能 synchronized (Java3y.class) { // 再判断一次是否为null if (java3y == null) { java3y = new Java3y(); } } } return java3y; } }
其实还不稳!这里会有重排序的问题:
原本想测试重排序问题的效果的,一直没测试出来~~~有相关测试代码的但愿能够告诉我怎么能测出来....
要解决也十分简单,加上咱们的volatile关键字就能够了,volatile有内存屏障的功能!
具体可参考资料:
因此说,完整的DCL代码是这样子的:
public class Java3y { private Java3y() { } private static volatile Java3y java3y = null; public static Java3y getJava3y() { if (java3y == null) { // 将锁的范围缩小,提升性能 synchronized (Java3y.class) { // 再判断一次是否为null if (java3y == null) { java3y = new Java3y(); } } } return java3y; } }
再说明:
还可使用静态内部类这种巧妙的方式来实现单例模式!它的原理是这样的:
getInstance()
时,都会使SingletonHolder被加载和被初始化,此时静态初始化器将执行Singleton的初始化操做。(被调用时才进行初始化!)public class Java3y { private Java3y() { } // 使用内部类的方式来实现懒加载 private static class LazyHolder { // 建立单例对象 private static final Java3y INSTANCE = new Java3y(); } // 获取对象 public static final Java3y getInstance() { return LazyHolder.INSTANCE; } }
静态内部类这种方式是很是推荐使用的!不少人没接触过单例模式的都不知道有这种写法,这种写法很优化也高效!
参考资料:
使用枚举就很是简单了:
public enum Java3y3y { JAVA_3_Y_3_Y, }
那这种有啥好处??枚举的方式实现:
这种也较为推荐使用!
总的来讲单例模式写法有5种:
明天估计写的是工厂模式了,敬请期待哦~~~
参考资料:
若是文章有错的地方欢迎指正,你们互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同窗,能够关注微信公众号:Java3y。为了你们方便,刚新建了一下qq群:742919422,你们也能够去交流交流。谢谢支持了!但愿能多介绍给其余有须要的朋友
文章的目录导航: