单例模式-懒汉式的四种方案
饿汉式
饿汉式的代码很简单也不是我们的重点。
package singleton_k;/* * @auther 顶风少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 20:41 * @notify * @version 1.0 */ public class Computer { private Computer() { } private static Computer computer = new Computer(); public static Computer getInstance() { return computer; } }
懒汉式-同步锁
这种synchronized关键字的做法是很最简单的,但是性能较差,对象的创建实际只有一次,剩下的其实都是获取,但是却要在获取时也要同步。
package singleton_k;/* * @auther 顶风少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 20:41 * @notify * @version 1.0 */ public class Computer2 { private Computer2() { } private static Computer2 computer = null; public static synchronized Computer2 getInstance() { if (computer == null) { computer = new Computer2(); } return computer; } }
懒汉式-双检查锁
将synchronized转移到方法中,假如有10条线程进入方法内,如果判断==null,也只能有一个线程进入同步代码块,进入代码块后再次判断是否==null第一个线程会将对象实例化。而后来的9条线程判断!=null就不会再次创建。第二次再来10条线程,则第一个if判断都不会通过,所以不会进入synchronized代码块也不会造成线程串行。大大的解决的性能问题。
package singleton_k;/* * @auther 顶风少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 20:41 * @notify * @version 1.0 */ public class Computer3 { private String name = "张三"; private Computer3() { } private static Computer3 computer = null; public static Computer3 getInstance() { //第二个线程判断对象不为null if (computer == null) { synchronized (Computer3.class) { if (computer == null) { computer = new Computer3(); } } } return computer; } }
但是这里还会有一点问题,如果在第一个线程new之后,但是没有初始化成员变量。第二个线程在第一次判断这时已经不是null了,它走到打印处,可能会没有值。所以使用volatile关键字。这个关键字的作用禁止CPU使用缓存,禁止指令重排序。
package singleton_k;/* * @auther 顶风少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 20:41 * @notify * @version 1.0 */ public class Computer3 { private String name = "张三"; private Computer3() { } /* volatile 作用1 禁止被他修饰变量发生指令重排序 作用2 禁止CPU使用缓存 * */ private static volatile Computer3 computer = null; public static Computer3 getInstance() { //第二个线程判断对象不为null if (computer == null) { synchronized (Computer3.class) { if (computer == null) { /*1 new:开辟内存空间 * 2 成员变量初始化 * 3 将内存中的地址赋值给变量 * 此处2和3可能指令重排序 * */ //第一个线程执行完下边这一句new出来了。 computer = new Computer3(); } } } //此处可能会出错,出错了。线程1的指令重排序是132但是2还没执行就拿不到这个变量的值了 System.out.println(computer.name); return computer; } }
懒汉式-静态内部类
通过静态内部类的方式实现单例模式是线程安全的,同时静态内部类不会在Singleton类加载时就加载,而是在调用getInstance()方法时才进行加载,达到了懒加载的效果。
package singleton_k;/* * @auther 顶风少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 20:41 * @notify * @version 1.0 */ public class Computer4 { private Computer4() { } /* * 通过内部类维护单例JVM在类加载的时候,是互斥的,所以可以保证线程安全问题 * */ private static class Computer4Factory{ private static Computer4 computer4 = new Computer4(); } public static Computer4 getComputer(){ return Computer4Factory.computer4; } }
懒汉式-枚举
利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题。除此之外,写法还特别简单。
package singleton_k;/* * @auther 顶风少年 * @mail dfsn19970313@foxmail.com * @date 2020-01-15 20:41 * @notify * @version 1.0 */ public enum Computer4 { INSTANCE; public void show(){ System.out.println("....."); } }