为什么没有人做像58一样的网站,深圳vi设计公司哪家好,片多多可以免费看电视剧吗,奔驰高端品牌一、什么是单例模式 单例模式#xff0c;属于创建类型的一种常用的设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例#xff08;根据需要#xff0c;也有可能一个线程中属于单例#xff0c;如#xff1a;仅线程上下文内使用同一个实例#xff09;。 对于系…一、什么是单例模式 单例模式属于创建类型的一种常用的设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例根据需要也有可能一个线程中属于单例如仅线程上下文内使用同一个实例。 对于系统中的某些类来说只有一个实例很重要例如一个系统中可以存在多个打印任务但是只能有一个正在工作的任务一个系统只能有一个文件系统一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化将弹出多个窗口如果这些窗口显示的内容完全一致则是重复对象浪费内存资源如果这些窗口显示的内容不一致则意味着在某一瞬间系统有多个状态与实际不符也会给用户带来误解不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。摘自百度百科 单例模式的应用场景有很多 1、数据库连接池的设计与实现 2、多线程的线程池设计与实现 3、Spring中创建的Bean实例默认都是单例 4、Java-Web中一个Servlet类只有一个实例……
二、单例模式实现
2.1、饿汉模式
public class SingularDemo3 {private static SingularDemo3 instance new SingularDemo3();private SingularDemo3() {System.out.println(SingularDemo3);}public static SingularDemo3 getInstance(){// 在程序启动的时候直接运行加载后续有外部需要使用的时候获取即可// 导致的问题就像你下载个游戏软件可能你游戏地图还没有打开呢但是程序已经将这些地图全部实例化return instance;}
}
可以看到上述代码很简单直接在启动时加载但是导致的就是很多没用到的东西他也给加载了造成不必要的负担。
2.2、懒汉模式
2.2.1、使用synchronized锁
public class SingularDemo2 {public static volatile SingularDemo2 instance;private SingularDemo2() { // 设置成私有不允许外面调用System.out.println(SingularDemo2);}public static synchronized SingularDemo2 getInstance(){if(instance ! null) return instance;return new SingularDemo2();}
}
这种方式是不用不加载用到的时候再加载使用synchronized加锁保证只有一个线程访问这种是线程安全的。但锁的粒度太大导致竞争激烈造成不必要的资源浪费。
同时这里的volatile是防止指令重排序导致导致对象未初始化完全。
2.2.2、使用双重校验锁
public class SingularDemo5 {public static volatile SingularDemo5 instance; // 要用volatile修饰防止指令重排序private SingularDemo5() {System.out.println(singularDemo5);}public static SingularDemo5 getInstance(){if(instance ! null) return instance; // 如果有直接返回synchronized (SingularDemo5.class){ // 锁住此对象if(instance ! null) return instance; // 进入之后再判断一次instance new SingularDemo5();return instance;}}
}
双重校验锁降低了锁的粒度只有当instance没有被初始化的时候第一个访问的线程才回去实例化它。这里内部还要有一个判断instance是否为null是为了防止同时多个线程竞争一个线程初始化instance之后其他线程也竞争到锁再去初始化instance所以进入临界区之后第一件事就是看看是否已经初始化好了。
这里的volatile有两个作用一是保证可见性其他线程读取的都是最新值二是防止指令重排序导致对象未初始化完全。
2.2.3、类的内部类
public class SingularDemo4 {public static class Handle{public static SingularDemo4 instance new SingularDemo4();}public SingularDemo4() {System.out.println(SingularDemo4);}public static SingularDemo4 getInstance(){return Handle.instance;}
}
类的内部类是靠JVM来保证并发的正确性也就是一个类的构造方法在多线程下可以被正常的加载。我们永远可以相信JVM。
那问题来了JVM是如何保证多线程下类的构造方法只会被执行一次的呢
1、类加载机制和初始化锁 当类加载器加载类时JVM会使用类加载锁Initialization Lock来确保在同一时刻只有一个线程对类进行加载和初始化操作。 一个线程开始初始化一个类时其他线程需要等待这个类初始化完成这是因为类的初始化过程中可能会执行静态代码块或静态变量赋值等操作保证这些操作的互斥性能够避免多线程环境下的干扰。
2、Happens-Before 在类的初始化过程中对静态变量的赋值操作在对象引用发布之前必须完成这意味着在静态变量赋值完成之前其他线程不会看到不完整或部分初始化的对象。
2.2.4、使用CAS
public class SingularDemo7 {private static final AtomicReferenceSingularDemo7 instance new AtomicReference();private SingularDemo7(){System.out.println(singularDemo7);}public static SingularDemo7 getInstance() {while (true) {SingularDemo7 singularDemo7 instance.get();if(singularDemo7 ! null) return singularDemo7;instance.compareAndSet(null, new SingularDemo7());return instance.get();}}
}使用CAS方式保证单例好处是不用加锁效率更高缺点也很明显竞争激烈的情况下可能大家都没法玩一直循环一直失败重试……
2.2.5、使用枚举类
public enum SingularDemo8 {instance;
}Effective Java 作者推荐使用枚举的方式解决单例模式。
三、小结 单例模式在实际的开发中应用的很多它确保一个类只有一个实例且这个实例的构造方法是私有的并提供一个全局访问点来获取该实例。 实现方式大致可以分为饿汉类和懒汉类饿汉就是程序启动时就加载这样有可能导致没有被用到的类也加载了导致了性能的浪费懒汉式的加载是用到就加载不用不加载但是要注意的是线程安全问题合理的使用synchronized和volatile来保证线程安全。 推荐使用的方式是双重校验锁和类的内部类这两种方式既能保证懒加载也能保证不会因为加锁或者锁的粒度较大导致的性能浪费。