暮年|20年架构师深入讲解java多线程与高并发:volatile与CAS,涨薪5K( 二 )


这个是通过实际工程验证了 , 不仅提高了 , 而且提高了很多 。 -DCL单例
我们来聊一聊什么是单例 , 单例的意思就是我保证你在JVM的内存里头永远只有某一个类的一个实例 , 其实这个很容易理解 , 在我们工程当中有一些类真的没有必要new好多个对象 , 比如说权限管理者 。
单例最简单的写法就是下面这种写法 , 是说我有一个类 , 定义了这个类的一个对象 , 然后一个对象呢
是在个类的内部的 , 同时我把Mgr01()这个类的构造方法设置成private意思就是别的不要去new我 , 只有我自己能new , 理论上来说我就只有自己一个实例了 , 通过getInstance()访问这个实例 , 所以无论你调用多少次的getInstanc()本质上它就只有这一个对象 , 这种写法非常简洁也很容易理解 , 由JVM来保证永远只有这一个实例 。
package com.mashibing.dp.singleton;/*** 饿汉式* 类加载到内存后 , 被实例化一个单例 , JVM保证线程安全* 简单实用 , 推荐使用!* 唯一缺点 , 不管用到与否 , 类装载时就完成实例化* Class.forName("")**/public class Mgr01{private static final Mgr01 INSTANCE = new Mgr01();private Mgr01(){};public static Mgr01 getInstance(){return INSTANCE;}public void m(){System.out.println("m");}public static void main(String[] args){Mgr01 m1=Mgr01.getInstance();Mgr01 m2=Mgr02.getInstance();System.out.println(m1==m2);}}但是有的人他会吹毛求疵 , 他会说我还没开始用这个对象呢 , 没用这个对象调这个方法你干嘛把他初始化了 , 你能不能什么时候开始用 , 调这个方法的时候你再给我初始化 。 所以呢 , 下面代码这个是和上一种一样的写法 。
package com.mashibing.dp.singleton;/*** 跟01是一个意思**/public class Mgr02{private static final Mgr02 INSTANCE;static {INSTANCE == new Mgr02();}private Mgr02(){};public static Mgr02 getInstance(){return INSTANCE;}public void m(){System.out.println("m");}public static void main(String[] args){Mgr02 m1=Mgr02.getInstance();Mgr02 m2=Mgr02.getInstance();System.out.println(m1==m2);}}所以另外产生这种懒汉式的单例 , 意思是说我getInstance() , 什么时候我开始调用这个getInstace()的时候 , 我才对它进行初始化 。 当然 , 这个不要对它进行初始化两次 , 只能初始化一次才对 , 不然就成了俩对象了吗 , 所以上来之后先判断INSTANCE == null 的话我才初始化 。
不过 , 更加吹毛求疵的事情又来了 , 我不单要求你我用的时候才进行初始化 , 我还要求你线程安全 。 显然我们下面03这个是不保证线程安全的 , 所以你多个线程访问的时候它一定会出问题 , 下来你自己可以实验实验 。
/*** 懒汉式单例*/public class Mgr03{private static Mgr03 INSTANCE;private Mgr03(){}public static Mgr03 getInstance(){if(INSTANCE == null){try{Thread.sleep(1);}catch(InterruptedException e){e.printStace();}INSTANCE = new Mgr03();}public void m(){System.out.println("m");}public static void main(String[] args){for(int i=0; i<100; i++){new Thread(()->System.out.println(Mgr03.getInstance().hashCode())).start();}}}}所以他要怎么做呢 , 我们要加一个synchronized解决 , 加一把锁嘛public static synchronized 这句话一旦加上就没问题了 , 因为这个里面从头到尾就只有一个线程运行 , 第一个线程发现它为空给它new了 , 第二个线程他无论怎么访问这个值已经永远不可能为空了 , 它只能是拿原来第一个线程初始化的部分 , 这是没问题的
/*** lazy loading* 也称懒汉式* 虽然达到了按需初始化的目的 , 但却能带来线程不安全的问题* 可以通过synchronized解决 , 但也带来了效率下降*/public class Mgr04{private static Mgr04 INSTANCE;private Mgr04(){}public static synchronized Mgr04 getInstance(){if(INSTANCE == null){try{Thread.sleep(1);}catch(InterruptedException e){e.printStace();}INSTANCE = new Mgr04();}public void m(){System.out.println("m");}public static void main(String[] args){for(int i=0; i