标签:package ima pre 实例化 sleep 在线 lin 锁对象 over
JVM将整个运行环境当做一个单例对象。
要点:
饿汉就是很着急,想吃东西,无论我当前要不要这些实例,它都着急着把它创建出来。
懒汉就是很懒,不到万不得已,不会去做,
我们先来总结一下刚刚提到的几点:
先来写饿汉式的单例,直接创建这个对象,无论是否需要它:
什么时候不需要这个静态对象呢?比如该类中含有静态方法,我可以通过类名直接调用,这时就没有这个对象啥事了,比如下图中的test()方法,但由于我们的INSTANCE是静态变量,在类加载时就已经创建,是不管test()用没用到的,都会创建出来。
在JDK1.5引入枚举之后,这种方式有更简洁的写法。
枚举类型,表示该类型对象是有限的几个,我们可以限定为一个,就成了单例。
非常简洁,但效果和我们之前写的第一种方法完全一样,获取方式也是可以通过类名.INSTANCE的方式获取。
第三种方式,看起来和第一种没啥太大区别,无非就是实例化使用静态代码块的方式实现,那我们什么时候回用到它?
比如说,我们有属性需要初始化:
这样写自然没问题,不过如果我们是通过配置文件的方式配置的,那该怎么办?
很显然,一二种方法很难做到这一点,第三种方法就可以在加载类时,将配置文件加载进来
为什么说饿汉模式不存在线程安全?因为JAVA中这些静态初始化的代码都会合成为<clinit>,<clinit>是线程安全方法,他们三个都是在类初始化的时候,直接创建的对象的。
接下来说说懒汉式:
要等到需要用这个单例时,我们才创建它,这样最初的时候instance应该是空的,为了避免外界在它还是空的时候通过类名.的方式获取,所以把instance设定为private。
在getInstance中,如果instance是空的,那么创建,如果不是,直接返回。
做一个测试:
Singleton4 singleton1 = Singleton4.getInstance(); Singleton4 singleton2 = Singleton4.getInstance(); System.out.println(singleton1==singleton2); System.out.println(singleton1); System.out.println(singleton2);
在单线程情况下,这个是没有问题的,如果是多线程呢?
也可以做一个测试,通过线程池的方式:
Callable<Singleton4> c = new Callable<Singleton4>() { @Override public Singleton4 call() throws Exception { return Singleton4.getInstance(); } }; ExecutorService es = Executors.newFixedThreadPool(2); Future<Singleton4> f1 = es.submit(c); Future<Singleton4> f2 = es.submit(c); Singleton4 s1 = f1.get(); Singleton4 s2 = f2.get(); System.out.println(s1==s2); System.out.println(s1); System.out.println(s2);
为了看出效果,我们在创建单例时加一个休眠代码:
public static Singleton4 getInstance() { if(instance==null) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } instance = new Singleton4(); } return instance; }
结果很显然,在线程1发现instance没有初始化时,会进入if块中初始化instance,在线程1还没有来得及创建instance时,切换到线程2,它也发现instance没有初始化,也进入if块中,因此出现了两个instance,不符合单例的要求
我们可以用一个很暴力的方式,直接用synchronize包住这些代码,将Singlgton.class作为锁对象
package singleton; public class Singleton5 { private static Singleton5 instance; private Singleton5() { } public static Singleton5 getInstance() { synchronized (Singleton5.class) { if (instance == null) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } instance = new Singleton5(); } return instance; } } }
做个测试:
当然这还不是最优版,最初来的时候,instance还没初始化,为了安全我们使用了synchronize,但是后面的获取行为就没有必要使用这个及其影响性能的synchronize了,所以用一个if可以大大提高性能
package singleton; public class Singleton6 { private static Singleton6 instance; private Singleton6() { } public static Singleton6 getInstance() { if(instance==null) { synchronized (Singleton6.class) { if (instance == null) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } instance = new Singleton6(); } } } return instance; } }
最后一种是又简单,又有效的方式,在静态内部类加载初始化时,才创建INSTANCE实例对象,静态内部类不会自动随着外部类的加载和初始化而初始化,它是要单独去加载初始化的
package singleton; public class Singleton7 { ; private Singleton7() { } private static class Inner{ private static final Singleton7 INSTANCE = new Singleton7(); } public static Singleton7 getInstance() { return Inner.INSTANCE; } }
标签:package ima pre 实例化 sleep 在线 lin 锁对象 over
原文地址:https://www.cnblogs.com/figsprite/p/10799576.html