zoukankan      html  css  js  c++  java
  • 设计模式(单例模式)

    1、概念

    (1)单例模式

    采用某种方法保证在整个软件设计中,某一个类只存在一个对象实例,并且该类只提供一个取得其对象的方法。Hibernate的SessionFactory不是轻量级的,采用的是单例模式,通常一个项目只有一个

    (2)实现方式

    饿汉式(静态常量)、饿汉式(静态代码块)、懒汉式(线程不安全)、懒汉式(线程安全、同步方法)、懒汉式(线程安全、同步代码块)、双重检查、静态内部类、枚举

    • 饿汉式可以使用,但是要保证实例一定会被使用,否则会造成内存的浪费
    • 推荐使用:双重检查、静态内部类、枚举三种
    • 懒汉式要注意线程安全的问题

    (3)使用场景

    单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建和销毁的对象,可以使用单例模式来提高系统的性能。当需要实例化一个单例类的时候,需要使用的是获取对象的方法,而不是采用new的方式

    • 需要进行频繁的创建和销毁的对象
    • 创建对象时耗时过多或消耗资源过多但又经常需要使用的对象
    • 工具类对象
    • 频繁访问数据库或文件的对象(如:数据源、session工厂等)

    2、饿汉模式

    (1)静态常量方式

    public class Mgr01 {
        private static final Mgr01 INSTANCE = new Mgr01();
    
        private Mgr01() {};
    
        public static Mgr01 getInstance() {
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            Mgr01 m1 = Mgr01.getInstance();
            Mgr01 m2 = Mgr01.getInstance();
            System.out.println(m1 == m2);
        }
    }
    true

    优点:写法比较简单,类加载到内存后就实例化一个对象,避免了线程同步的问题

    缺点:没有达到懒加载的效果,如果对象未得到使用就会造成内存的浪费

    (2)静态代码块方式

    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);
        }
    }

    只是类实例化的代码放到了静态代码块中,优缺点和静态常量方式一样

    (3)懒汉式(线程不安全)

    public class Mgr03 {
        private static Mgr03 INSTANCE;
    
        private Mgr03() {
        }
    
        public static Mgr03 getInstance() {
            if (INSTANCE == null) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Mgr03();
            }
            return INSTANCE;
        }
    
        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();
            }
        }
    }
    1850443050
    1052444169
    2040686731
    1919392881
    1887032973
    1887032973
    145632894
    145632894
    1219201818
    1413709757
    1413709757
    1413709757
    1413709757
    1413709757

    刚开始多个线程都走到了判断对象是否为空的语句,便会产生多个对象,也就是说此种方式是线程不安全的

    (4)懒汉式(线程安全、同步方法)

    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.printStackTrace();
                }
                INSTANCE = new Mgr04();
            }
            return INSTANCE;
        }
    
        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(Mgr04.getInstance().hashCode());
                }).start();
            }
        }
    }

    将获取实例的方法换成同步方法,但是效率会下降,且该方法执行一次就够了,后面直接返回对象

    (5)减小同步区域,但是不可行

    public class Mgr05 {
        private static Mgr05 INSTANCE;
    
        private Mgr05() {
        }
    
        public static Mgr05 getInstance() {
            if (INSTANCE == null) {
                synchronized (Mgr05.class) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new Mgr05();
                }
            }
            return INSTANCE;
        }
        public static void main(String[] args) {
            for(int i=0; i<100; i++) {
                new Thread(()->{
                    System.out.println(Mgr05.getInstance().hashCode());
                }).start();
            }
        }
    }

    不再采用同步方法的方式来提高代码的效率,但是会带来线程安全的问题,因为在多线程的情况下依旧会有多个线程执行到非空判断语句

    (6)双重检查

    public class Mgr06 {
        private static volatile Mgr06 INSTANCE; 
    
        private Mgr06() {
        }
    
        public static Mgr06 getInstance() {
            if (INSTANCE == null) {
                //双重检查
                synchronized (Mgr06.class) {
                    if(INSTANCE == null) {
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        INSTANCE = new Mgr06();
                    }
                }
            }
            return INSTANCE;
        }
    
        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(Mgr06.getInstance().hashCode());
                }).start();
            }
        }
    }

    即使有多个线程跨过了非空判断,但是只有第一个线程能跨过第二层的非空判断,因为volatile修饰后的变量修改后能够立即同步回主内存,第二个线程执行的时候已经不为空

    优点:懒加载,效率较高、线程安全、只创建一次实例

    (7)静态内部类

    public class Mgr07 {
    
        private Mgr07() {
        }
    
        private static class Mgr07Holder {
            private final static Mgr07 INSTANCE = new Mgr07();
        }
    
        public static Mgr07 getInstance() {
            return Mgr07Holder.INSTANCE;
        }

    public static void main(String[] args) { for(int i=0; i<100; i++) { new Thread(()->{ System.out.println(Mgr07.getInstance().hashCode()); }).start(); } } }

    根据静态内部类外部类加载的时候内部类不会被加载的特点,实现了懒加载,内部类只有在调用INSTANCE变量的时候才会被装载

    且在内部类装载的时候是单线程的,保证了线程的安全

    (8)枚举

    public enum Mgr08 {
    
        INSTANCE;
    
        public void m() {}
    
        public static void main(String[] args) {
            for(int i=0; i<100; i++) {
                new Thread(()->{
                    System.out.println(Mgr08.INSTANCE.hashCode());
                }).start();
            }
        }
    }

    不存在线程安全的问题、能够防止反序列化重新创建新的对象

    3、在JDK中的使用

    public class Runtime {
        private static Runtime currentRuntime = new Runtime();
    
        /**
         * Returns the runtime object associated with the current Java application.
         * Most of the methods of class <code>Runtime</code> are instance
         * methods and must be invoked with respect to the current runtime object.
         *
         * @return  the <code>Runtime</code> object associated with the current
         *          Java application.
         */
        public static Runtime getRuntime() {
            return currentRuntime;
        }
    每个人都会有一段异常艰难的时光 。 生活的压力 , 工作的失意 , 学业的压力。 爱的惶惶不可终日。 挺过来的 ,人生就会豁然开朗。 挺不过来的 ,时间也会教你 ,怎么与它们握手言和 ,所以不必害怕的。 ——杨绛
  • 相关阅读:
    每日一练leetcode
    每日一练leetcode
    每日一练 leetcode
    每日一练leetcode
    每日一练leetcode
    leetcode每日一练
    BigDecimal类 定义及用法
    每日一练leetcode
    每日一练leetcode
    字符串表达式校验&求值(C#实现) 附代码
  • 原文地址:https://www.cnblogs.com/zhai1997/p/14403997.html
Copyright © 2011-2022 走看看