当系统中某个类对象只须要实例化一次的时候,咱们就用单例来实现。因此单例模式就是用来建立独一无二,只能有一个实例的对象的一直实现方式。数据库
好比线程池,缓存,链接数据库的Connection等等。缓存
1,饿汉式安全
package singleton; /** * @ClassName SingletonDemo1 * @Description 饿汉式 * 类加载到内存后,就实例化一个单例对象,JVM保证线程安全 * 惟一缺点:无论用不用都会进行加载,可是影响不大,其实是最适用的一种方式 * @Author liuyi * @Date 2020/6/7 12:22 * @Version 1.0 */ public class SingletonDemo1 { //两种方式初始化实例,两种方式的效果是同样的 //静态常量方式 // private static final SingletonDemo1 instance = new SingletonDemo1(); //静态块方式 private static final SingletonDemo1 instance; static { instance = new SingletonDemo1(); } private SingletonDemo1(){ } public static SingletonDemo1 getInstance(){ return instance; } public static void main(String[] args) { SingletonDemo1 instance1 = SingletonDemo1.getInstance(); SingletonDemo1 instance2 = SingletonDemo1.getInstance(); System.out.println(instance1==instance2); //返回的结果为true,说明无论取多少次都是同一个实例 } }
2,懒汉式多线程
package singleton; /** * @ClassName SingletonDemo2 * @Description 懒汉式 * 须要使用该对象的时候再去实例化 * 缺点:会产生线程安全问题 * @Author liuyi * @Date 2020/6/7 13:23 * @Version 1.0 */ public class SingletonDemo2 { private static SingletonDemo2 instance; private SingletonDemo2(){ } public static SingletonDemo2 getInstance(){ if(instance==null){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } instance = new SingletonDemo2(); } return instance; } public static void main(String[] args) { //为何说线程不安全 //由于使用了全局的静态变量,会形成多线程访问的时候会产生不惟一的实例 for (int i = 0; i <100 ; i++) { new Thread(()->{ System.out.println(getInstance()); }).start(); } //打印的对象彻底乱了,根本不是同一个实例 } }
3,饿汉式(加锁)ide
package singleton; /** * @ClassName SingletonDemo2 * @Description 懒汉式(加锁) * 缺点:效率低 * @Author liuyi * @Date 2020/6/7 13:23 * @Version 1.0 */ public class SingletonDemo3 { private static SingletonDemo3 instance; private SingletonDemo3(){ } public synchronized static SingletonDemo3 getInstance(){ if(instance==null){ instance = new SingletonDemo3(); } return instance; } public static void main(String[] args) { //加了锁解决了懒汉式的线程不安全问题,可是这样效率就会明显下降 for (int i = 0; i <100 ; i++) { new Thread(()->{ System.out.println(getInstance()); }).start(); } } }
4,双重检查测试
package singleton; /** * @ClassName SingletonDemo2 * @Description 双重检查(比较完美的写法) * @Author liuyi * @Date 2020/6/7 13:23 * @Version 1.0 */ public class SingletonDemo4 { //必须加volatile关键字,防止指令重排 private static volatile SingletonDemo4 instance; private SingletonDemo4(){ } public synchronized static SingletonDemo4 getInstance(){ //为何要进行双重检查 //好比两个线程同时进入该方法,都拿到instance为空,其中一个拿到锁并new了一个实例, //此时另一个线程它并不知道你已经new了实例,因此当它拿到锁以后会继续new一个实例 //因此若是在锁里面继续判断一次是颇有必需要的 if(instance==null){ synchronized (SingletonDemo4.class){ if(instance==null){ instance = new SingletonDemo4(); } } } return instance; } public static void main(String[] args) { for (int i = 0; i <100 ; i++) { new Thread(()->{ System.out.println(getInstance()); }).start(); } } }
5,静态内部类方式spa
package singleton; /** * @ClassName SingletonDemo5 * @Description 静态内部类方式 * JVM保证线程安全 * 加载外部类是不会加载内部类,实现了懒加载 * 最完美的写法 * @Author liuyi * @Date 2020/6/7 13:52 * @Version 1.0 */ public class SingletonDemo5 { private SingletonDemo5(){ } private static class SingletonDemo5Inside{ private static final SingletonDemo5 instance = new SingletonDemo5(); } public static SingletonDemo5 getInstance(){ return SingletonDemo5Inside.instance; } public static void main(String[] args) { for (int i = 0; i <100 ; i++) { new Thread(()->{ System.out.println(SingletonDemo5.getInstance()); }).start(); } } }
6,枚举方式线程
package singleton; /** * @Author liuyi * @Description 枚举单例 * 不只能够解决线程同步,还能够防止反序列化(由于枚举类没有构造方法) * @Date 17:28 2020/6/7 * @Param * @return **/ public enum SingletonDemo6 { instance; public void test(){ System.out.println("测试测试"); } public static void main(String[] args) { instance.test(); for (int i = 0; i <100 ; i++) { new Thread(()->{ System.out.println(SingletonDemo6.instance); }).start(); } } }
这几种实现单例的方式中,枚举单例是最完美的,由于枚举单例能够防止反序列话,也能够防止经过反射的方式去建立实例,可是实际运用中最适用的仍是饿汉式。由于既然你使用了单例,为何还要用反射呢,这样就属于搞破坏了。code