java设计模式之单例模式实现(通俗易懂)

由于我们java开发中,会看到依赖注入,还有自动注入的这些注解方式,对于一些初学者,或者平时不怎么研究spring一些技术的原理的话,可能不太清楚,这里我就简单说下,一般依赖注入,还有自动注入,通常会在启动项目的时候,就已经把所有bean扫描了,并且注册了,另外也会默认使用单例模式创建每个接口的实现类,然后我们调用@autowire注解的时候,就会实现自动注入,所以知道了这些,大家就了解一下原理,拓展一下眼界,

先放出简单的实现代码

package com.vincent.DesignPattern;

public class SingleTonTest {

    /***
     * spring使用自动注入的时候就实现单例模式
     * 在通用的SSH中,单例在spring中是默认的,如果要产生多例,则在配置文件的bean中添加scope="prototype";
     * 它们都不对外提供构造方法,即构造方法都为私有。
     * singleton(单例):只有一个共享的实例存在,所有对这个bean的请求都会返回这个唯一的实例。
     *
     * prototype(多例):对这个bean的每次请求都会创建一个新的bean实例,类似于new。
     *
     * 单例设计模式的一般定义:一个类中只允许有一个实例。 实现思路:让类的构造方法私有化,同时提供一个静态方法去实例化这个类。
     *
     * 懒汉式:在静态方法中初始化。时间换空间。(不推荐,时间很重要) 饿汉式:在声明对象就初始化。空间换时间。(推荐,空间不是问题)
     *
     * 懒汉式线程不安全,需要加上同步锁,同步锁影响了程序执行效率 饿汉式天生线程安全,类加载的时候初始化一次对象,效率比懒汉式高。
     *
     * 注意私有构造方法
     */
    // 定义成私有构成方法,变成单例的 单例模式的核心
    private SingleTonTest() {}

    // 饿汉式:类加载的时候即进行初始化
    private static final SingleTonTest singleTon = new SingleTonTest();

    public static SingleTonTest getSingleTon() {
        return singleTon;
    }

    /******************************* 分割线 *********************************/
    // 懒汉式 双重校验锁保证线程安全,比较好的写法  --- volatile 禁止指令重排 主要由于new SingleTon();可能出现问题
    private volatile static SingleTonTest singleTonHungry = null;

    public static SingleTonTest getSingleTonHungry() {
        if (singleTonHungry == null) {
            synchronized (SingleTonTest.class) {
                if (singleTonHungry == null) {
                    singleTonHungry = new SingleTonTest();
                }
            }
        }
        return singleTonHungry;
    }


 
}

1.保证线程修改的可见性
Java语言编写的程序,有时为了提高运行效率,编译器会自动对其优化,把经常访问的变量缓存起来,程序在读取这个变量时有可能直接从缓存(例如寄存器)中读取,而不会去内存中读取。当多线程编程时,变量的值可能因为别的线程改变了,而该缓存的值不会相应的改变,从而造成读取的值与实变量的值不一致。
volatile 被设计用来修饰不同线程访问和修改的变量。被volatile 类型定义的变量,系统每次用到他时都直接从对应的内存中提取,而不会利用缓存。这样所有线程在任何时候拿到的变量的值都是相同的。

2.禁止指令重排序
在Java内存模型(JMM)中,并不限制处理器的指令顺序,说白了就是在不影响结果的情况下,顺序可能会被打乱。
在执行sInstance = new Singleton();这条命令语句时,JMM并不是一下就执行完毕的,即不是原子性,实质上这句命令分为三大部分:
1. 为对象分配内存
2. 执行构造方法语句,初始化实例对象
3. 把sInstance的引用指向分配的内存空间
在JMM中这三个步骤中的2和3不一定是顺序执行的,如果线程A执行的顺序为1、3、2,在第2步执行完毕的时候,恰好线程B执行第一次判空语句,则会直接返回sInstance,那么此时获取到的sInstance仅仅只是不为null,实质上没有初始化,这样的对象肯定是有问题的!
而volatile关键字的存在意义就是保证了执行命令不会被重排序,也就避免了这种异常情况的发生,所以这种获取单例的方法才是真正的安全可靠!
 

你可能感兴趣的:(java设计模式之单例模式实现(通俗易懂))