zoukankan      html  css  js  c++  java
  • 单例模式

    单例模式

    什么是单例模式

    比如一个团队多个开发者都需要操作日志文件,如果每个人都实例化一个日志对象,会导致不必要的开销。为了解决这类问题,单例模式产生了。单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。Windows系统中的回收站就是典型的一种单例模式应用,多次打开回收站不会创建新的窗口。

    单例模式的优缺点

    优点:
    • 节省内存空间。
    • 避免了频繁的创建销毁对象,可以提高性能。
    • 避免对共享资源的多重占用,简化访问。
    • 为整个系统提供一个全局访问点。
    缺点:
    • 不适于变化频繁的独享。
    • 为了节省资源将数据连接池对象设计为单例类,可能会导致共享连接池对象太多,出现连接池溢出。
    • 如果实例化的对象长时间不被使用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失。

    单例模式的实现

    1、饿汉式:类加载到内存后,就实例化一个单例,JVM保证线程安全

    package SingletonPattern;
    
    public class Singleton01 {
        private static final Singleton01 INSTANCE = new Singleton01();
    
        private Singleton01(){};
    
        public static Singleton01 getInstance(){return INSTANCE;}
    
        public void method(){
            System.out.println("Happy Everyday");
        }
    
        public static void main(String[] args) {
            Singleton01 m1 = Singleton01.getInstance();
            Singleton01 m2 = Singleton01.getInstance();
            System.out.println(m1 == m2);
        }
    }
    

    2、懒汉式:不在类加载的时候初始化,按需初始化

    package SingletonPattern;
    
    import org.omg.PortableServer.THREAD_POLICY_ID;
    
    public class Singleton02 {
        //不能定义为final型,因为常量需要初始化
        private static Singleton02 INSTANCE;
    
        private Singleton02(){
    
        }
    
        public static Singleton02 getInstance(){
            if(INSTANCE == null){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Singleton02();
            }
            return INSTANCE;
        }
    
        public void method(){
            System.out.println("Happy Everyday");
        }
    
        public static void main(String[] args) {
            for(int i = 0; i < 10; i++){
    //同一类的不同对象hashcode是不同的
                new Thread(()->{
                    System.out.println(Singleton02.getInstance().hashCode());
                }).start();
            }
        }
    }
    
    
    结果
    1184893857
    219573733
    449584288
    1924898670
    103512977
    585605895
    967740059
    1802506717
    499531361
    1416599099
    

    当多线程操作时,懒汉模式会出现,线程不安全的问题,比如A,B两个线程同时首次创建单例对象,A线程进入判空条件,创建对象之前,B线程此时INSTANCE依然为空,此时A,B线程会创建两个对象。

    3、在懒汉式上添加同步方法:解决懒汉模式线程不安全的问题

    package SingletonPattern;
    
    import org.omg.PortableServer.THREAD_POLICY_ID;
    
    public class Singleton03 {
        //不能定义为final型,因为常量需要初始化
        private static Singleton03 INSTANCE;
    
        private Singleton03(){
    
        }
    
        public static synchronized Singleton03 getInstance(){
            if(INSTANCE == null){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Singleton03();
            }
            return INSTANCE;
        }
    
        public void method(){
            System.out.println("Happy Everyday");
        }
    
        public static void main(String[] args) {
            for(int i = 0; i < 10; i++){
                new Thread(()->{
                    System.out.println(Singleton03.getInstance().hashCode());
                }).start();
            }
        }
    }
    
    结果
    1416599099
    1416599099
    1416599099
    1416599099
    1416599099
    1416599099
    1416599099
    1416599099
    1416599099
    1416599099
    
    

    4、双重检查法:虽然通过synchronized解决了线程不安全的问题,但是也带了效率的下降。在同步块中添加判空条件,否则A,B线程拿到锁后都会创建一个对象。

    package SingletonPattern;
    
    import org.omg.PortableServer.THREAD_POLICY_ID;
    
    public class Singleton04 {
        //不能定义为final型,因为常量需要初始化
        private static volatile Singleton04 INSTANCE;
    
        private Singleton04(){
    
        }
    
        public static synchronized Singleton04 getInstance(){
            if(INSTANCE == null){
                synchronized (Singleton04.class) {
                    if(INSTANCE == null) {
                        INSTANCE = new Singleton04();
                    }
                }
            }
            return INSTANCE;
        }
    
        public void method(){
            System.out.println("Happy Everyday");
        }
    
        public static void main(String[] args) {
            for(int i = 0; i < 10; i++){
                new Thread(()->{
                    System.out.println(Singleton04.getInstance().hashCode());
                }).start();
            }
        }
    }
    

    5、静态内部类方式:静态内部类在类加载的时候不会加载,在调用getInstance时加载,实现了懒加载。

    package SingletonPattern;
    
    import sun.applet.AppletResourceLoader;
    
    /**
     * 静态内部类方式
     */
    public class Singleton05 {
        private Singleton05(){};
    
        private static class Singleton05Holder{
            private final static Singleton05 INSTANCE = new Singleton05();
        }
    
        public static Singleton05 getInstance(){
            return Singleton05Holder.INSTANCE;
        }
    
        public void method(){
            System.out.println("Happy Everyday");
        }
    
        public static void main(String[] args) {
            for(int i = 0; i < 10; i++){
                new Thread(()->{
                    System.out.println(Singleton05.getInstance().hashCode());
                }).start();
            }
        }
    }
    

    6、枚举实现:枚举类是一个抽象类,没有构造方法,只能有一个实例,不仅可以解决线程同步,还可以防止反序列化。

    package SingletonPattern;
    
    public enum Singleton06 {
        INSTANCE;
    
        public void method(){
            System.out.println("Happy Everyday");
        }
    
        public static void main(String[] args) {
            for(int i = 0; i < 10; i++){
                new Thread(()->{
                    System.out.println(Singleton05.getInstance().hashCode());
                }).start();
            }
        }
    }
    
  • 相关阅读:
    微软一站式示例代码库(中文版)20110924版本, 新添加ASP.NET, Windows Base, Silverlight, WinForm等20个Sample
    Expression Blend实例中文教程系列文章汇总
    【分享】Silverlight 探秘系列课程汇总(下载)
    【分享】分享几个不错的富文本编辑器插件
    推荐 10 个优秀的 HTML5 编码工具
    SQLite数据库操作类
    每天写出好代码的5个建议
    ASP.NET中26个常用性能优化方法
    ConnectString中enlist设置的含义
    使用VS2008打开VS2010的解决方案
  • 原文地址:https://www.cnblogs.com/happysml/p/13849959.html
Copyright © 2011-2022 走看看