Java单例模式

单例模式之饿汉模式:

/**
 * 
 * @author Taowd
 * 功        能:单例模式,饿汉模式,线程安全,效率比较低
 * 优势:写法简单,线程安全
 * 缺点:加载速度比较慢,某些特定状况下会耗费内存
 * 编写时间:2017-5-11-上午8:37:37
 */
public class Singleton2 {

    // 私有化构造参数
    private Singleton2() {

    }

    // 将自身实力对象设置一个属性,并加上static和final修饰符
    private static final Singleton2 singleton2 = new Singleton2();

    // 静态方法返回该类的示例
    public static Singleton2 getInstance() {
        return singleton2;
    }
}

单例模式之懒汉模式:

package com.taowd.singleton;

/**
 * 
 * @author Taowd
 * 功能:单例模式,懒汉模式(饱汉模式),非线程安全的
 * 优势:编写简单,类加载速度快,使用速度慢
 * 缺点:并发环境下可能出现多个Singleton实例
 * 编写时间:2017-5-11-上午8:27:25
 */
public class Singleton1 {
    // 把构造方法私有化,防止经过new Singleton() 去实例化
    private Singleton1() {

    }

    // 定义一个Singleton类型的示例,不进行初始化,注意这里没有使用final关键字
    private static Singleton1 singleton;

    // 定义一个静态方法,外部调用时再初始化Singleton,可是多线程访问时可能形成重复初始化的问题
    public static Singleton1 getInstance() {
        if (singleton == null) {
            singleton = new Singleton1();
        }
        return singleton;
    }
}

单例模式之终极模式:

package com.taowd.singleton;

/**
 * 
 * @author Taowd
 * 功能:最终版,最优方案
 * 优势:内存占用低,效率高,线程安全,多线程操做原子性
 * 编写时间:2017-5-11-上午8:43:08
 */
public class Singleton {
    // 把构造方法私有化,防止经过new Singleton() 去实例化
    private Singleton() {

    }

    // 定义一个Singleton类型的示例,不进行初始化,注意这里没有使用final关键字
    // volatile 保证了多线程访问instance变量的可见性,避免了instance初始化时,其余变量属性还没赋值完就被另外线程调用
    // volatile:
    private static volatile Singleton instance;

    // 定义一个静态方法,外部调用时再初始化Singleton,可是多线程访问时可能形成重复初始化的问题
    public static Singleton getInstance() {
        // 对象实例化与否判断(不适用同步代码,instance不等于null时,直接返回对象,提升执行效率)
        if (instance == null) {
            // 同步代码块(对象未初始化时,使用同步代码,保证多线程访问对象在第一次被建立后,再也不重复被建立)
            // synchronized:
            synchronized (Singleton.class) {
                // 未初始化,则初始化instance变量
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

单例模式的测试程序:

package com.taowd.test;

import org.junit.Test;

import com.taowd.singleton.Singleton1;

public class SingletonTest {

    @Test
    public void TestSingleton() {
        Singleton1 singleton1 = Singleton1.getInstance();
        Singleton1 singleton2 = Singleton1.getInstance();
        System.out.println(singleton1 == singleton2 ? "同一对象" : "不是同一对象");
    }
}

  之前对单例模式不是很清楚,经过这几个例子也学习了一下单例模式,其中对volatile关键字的含义的不是很明白,由于不多用到这个关键字,查阅了一下资料下面将个人查阅的信息简单说明一下。缓存

  volatile关键字能够保证可见性和执行顺序,可见性既是先发生的原子修改操做必定会在读操做以前执行完成(同一时间对volatile变量只能有一个操做);执行顺序的含义既是volatile关键字会阻止编译器或JVM对代码重排序,或把volatile变量从同步区域(synchronized方法,synchronized代码块..)中移除出来。安全

Java的内存模型

Java的内存模型(JMM)能够保证一个线程修改了某个变量,该变量的改变对另外一个线程是可见的,也就是说另外一个线程取得的这个变量的值,必定是前一个线程修改以后的值。这个也被称做"happens-before" 。多线程

volatile关键字使用场所

volatile关键字只能修饰变量,不能修饰类,也不能修饰方法。并发

想要把某个变量共享,该变量的读写操做必须是原子性的,并用volatile关键字修饰。app

  • volatile修饰的long和double类型的变量读写操做是原子性的。long和double都是64位的,给long和double类型的变量赋值跟平台相关,在有些平台上不是原子操做。不少平台给long和double变量赋值须要2步操做,每一步只写32位,在这2个步骤之间,其余线程获取的long或double类型的变量的值的状态是不正确的。性能

  • volatile变量有相似synchronized同步代码的可见性,即每一个线程读取到的都是最新更新的值。volatile的局限性很大,你只能获取volatile变量的值,直接设置volatile变量的值,不能比较以后再设置volatile的值,由于在你作比较操做的区间颇有可能有其余线程修改了该volatile变量的值。学习

  • 被volatile关键字修饰,代表该变量是要被多个线程访问的,编译器不会对与volatile变量相关的代码作重排或其余多线程下不容许的优化。没有被volatile关键字修饰,编译器会重排代码,缓存变量的值,减小从主存中直接获取变量的值。测试

用例子来详细完全说明Java的volatile关键字工做原理

不用volatile关键字修饰,有可能某个线程能够获取到另外一个线程设置的isActive的值,编译器有可能缓存了isActive的值等等,使用volatile关键字能够避免这些状况。优化

  • 双重检查单例模式在JDK4是有问题的,volatile能够修复这个问题。本文的《第一个例子说明volatile变量的含义》中举的例子就是说这个问题。spa

volatile关键字的总结

1. volatile关键字只用修饰变量,不能修饰方法和类。

2. volatile变量的值都是从主存中获取的,而不是从线程的本地内存。

3.long和double变量被volatile关键字修饰以后,读写(赋值操做,读取操做)都是原子操做.

4. 使用volatile关键字能够避免内存不一致的错误;写入volatile变量必定会比接下来的读操做先发生。5. 从jdk5开始对volatile变量的修改对其余的线程都是可见的;当线程读取volatile变量的时候,会先把其余线程中缓存着的volatile变量(若是尚未更新到主存中的时候)强制写入到主存。

6. 除了long和double其余的基本类型读写操做都是原子性的;引用类型的读写操做也是原子性的。

7. volatile变量只能作简单的读写,没有锁,没有阻塞。

8.volatile变量能够是空的.

9. volatile不能保证原子性,好比volatile修饰的int变量++操做仍是非原子的。

10. 变量没有在多个线程之间共享,没有必要作任何同步的操做,好比使用volatile关键字修饰。

synchronized和volatile关键字的比较

volatile关键字代替不了synchronized关键字,不过在某些场合能够做为替代方案。

1. volatile关键字只能修饰字段,而synchronized只能修饰代码块和方法。

2. synchronized关键字须要得到锁释放锁,volatile关键字不须要。

3.synchronized代码块或方法在等待锁的时候会被阻塞;volatile不是这样的。

4. synchronized代码块或方法会比volatile关键字更影响性能。

5. volatile关键只同步被修饰的变量,而synchronized关键字却同步代码块或方法中全部的变量,而且还会得到锁释放锁,因此synchronized的负载更大。

6. 不能同步(synchronized)null对象,而volatile变量能够是null的。

7. 读取volatile变量效果等同获取锁,写入volatile变量效果等同释放锁。

相关文章
相关标签/搜索