设计模式-单例模式

概述:

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

优点:

1.单例模式只存在一个实例
2.节约资源,需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能
3.避免对共享资源的多重占用

缺点:

1.扩展困难
2.职责过重,一定程度上违反了“单一原则”
3.不适用于需要变化的对象

注意事项:

1.使用时不能用反射模式创建单例,否则会实例化一个新的对象 
2.使用懒单例模式时注意线程安全问题 
3.饿单例模式和懒单例模式构造方法都是私有的,因而是不能被继承的,有些单例模式可以被继承(如登记式模式) 

单例的要素:

   a.私有构造方法 
   b.私有静态引用指向自己实例 
   c.以自己实例为返回值的公有静态方法 

单例的介绍:

1.饿汉式:单例实例在类装载时就构建,急切初始化。(预先加载法) 

public class Test {

        //私有构造方法
        private Test() {

        }
        //在成员位置创建该类的对象
        public static Test instance = new Test();
        //对外提供静态方法获取该对象
        public static Test getInstance() {
                return instance;
        }

}

优点 
    1.线程安全 (类加载的方式是按需加载,且只加载一次)
    2.在类加载的同时已经创建好一个静态对象,调用时反应速度快 
缺点 

    资源效率不高,可能getInstance()永远不会执行到,但执行该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化 

2.懒汉:

单例实例在第一次被使用时构建,延迟初始化。 

class Test {

        private Test() {

        }

        public static Test instance = null;

        public static Test getInstance() {

                if (instance == null) {

              //多个线程判断instance都为null时,在执行new操作时多线程会出现重复情况

                        instance = new Singleton2();

                }

                return instance;

        }

}


优点: 
    避免了饿汉式的那种在没有用到的情况下创建事例,资源利用率高,不执行getInstance()就不会被实例,可以执行该类的其他静态方法。 
缺点: 
    懒汉式在单个线程中没有问题,但多个线程同事访问的时候就可能同事创建多个实例,而且这多个实例不是同一个对象,虽然后面创建的实例会覆盖先创建的实例,但是还是会存在拿到不同对象的情况。解决这个问题的办法就是加锁synchonized,第一次加载时不够快,多线程使用不必要的同步开销大。

3.懒汉-双重检测 

/**
 * 双重检查方式
 */
public class Singleton { 
​
    //私有构造方法
    private Singleton() {}
​
    private static Singleton instance;
​
   //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        //第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例
        if(instance == null) {
            synchronized (Singleton.class) {
                //抢到锁之后再次判断是否为null
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,上面的双重检测锁模式看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。

要解决双重检查锁模式带来空指针异常的问题,只需要使用 volatile 关键字, volatile 关键字可以保证可见性和有序性。

/**
 * 双重检查方式
 */
public class Singleton {
​
    //私有构造方法
    private Singleton() {}
​
    private static volatile Singleton instance;
​
   //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        //第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际
        if(instance == null) {
            synchronized (Singleton.class) {
                //抢到锁之后再次判断是否为空
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

        添加 volatile 关键字之后的双重检查锁模式是一种比较好的单例实现模式,能够保证在多线程的情况下线程安全也不会有性能问题。

4.懒汉-静态内部类 

class Test {

        private Test() {

        }

        private static class SingletonHelp {

                static Test instance = new Test();

        }

        public static Test getInstance() {

                return SingletonHelp.instance;

        }

}

优点 

    资源利用率高,不执行getInstance()不被实例,可以执行该类其他静态方法 
缺点 
    第一次加载时反应不够快 

5.枚举单例

/**
 * 枚举实现单例
 * 基于JVM底层实现, Enum天然的单例以及线程安全
 *(推荐).
 */
public enum EnumSingleton {

    /**
     * 构造方法默认为private
     */
    INSTANCE;

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "EnumSingleton{" +
                "name='" + name + '\'' +
                '}';
    }
}

优点:高效,线程安全,可以避免被反射破解单例

JDK源码解析-Runtime类

通过源代码查看使用的是哪儿种单例模式

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;
    }
​
    /** Don't let anyone else instantiate this class */
    private Runtime() {}
    ...
}

从上面源代码中可以看出Runtime类使用的是恶汉式(静态属性)方式来实现单例模式的。

使用Runtime类中的方法

public class RuntimeDemo {
    public static void main(String[] args) throws IOException {
        //获取Runtime类对象
        Runtime runtime = Runtime.getRuntime();
​
        //返回 Java 虚拟机中的内存总量。
        System.out.println(runtime.totalMemory());
        //返回 Java 虚拟机试图使用的最大内存量。
        System.out.println(runtime.maxMemory());
​
        //创建一个新的进程执行指定的字符串命令,返回进程对象
        Process process = runtime.exec("ipconfig");
        //获取命令执行后的结果,通过输入流获取
        InputStream inputStream = process.getInputStream();
        byte[] arr = new byte[1024 * 1024* 100];
        int b = inputStream.read(arr);
        System.out.println(new String(arr,0,b,"gbk"));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值