设计模式(单例模式)

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;
    }
每个人都会有一段异常艰难的时光 。 生活的压力 , 工作的失意 , 学业的压力。 爱的惶惶不可终日。 挺过来的 ,人生就会豁然开朗。 挺不过来的 ,时间也会教你 ,怎么与它们握手言和 ,所以不必害怕的。 ——杨绛
原文地址:https://www.cnblogs.com/zhai1997/p/14403997.html