Java : volatile 关键字

Volatile

单例设计模式的本质在于构造方法被私有化, 而后通过类内部的 static 方法取得实例化对象, 而单例设计有两种模式: 懒汉式(使用时才进行实例化对象), 饿汉式(在类加载时候进行对象实例化).

范例: 观察懒汉式单例设计模式的问题

package com.beyond.dhl;


class Singleton {
    
    
    private static Singleton instance;   // 懒汉式所以不会进行实例化对象

    private Singleton() {
    
    
        System.out.println("构造方法:" + Thread.currentThread().getName());
    }

    public static Singleton getInstance() {
    
    
        if (instance == null) {
    
    
            instance = new Singleton();
        }
        return instance;
    }


}

public class Test {
    
    
    public static void main(String[] args) {
    
    
        new Thread(() -> Singleton.getInstance(), "线程A").start();
        new Thread(() -> Singleton.getInstance(), "线程B").start();
        new Thread(() -> Singleton.getInstance(), "线程C").start();
        new Thread(() -> Singleton.getInstance(), "线程D").start();
        new Thread(() -> Singleton.getInstance(), "线程E").start();
    }
}

在这里插入图片描述
这个时候, 我们发现一个神奇的情况, 单例设计不再是单例了, 而变成了多个重复的实例化对象的类.
造成这种问题的关键是线程的不同步的问题. 有可能有多个线程同时进入到了getInstance()方法, 那么就有可能实例化多个对象.

那么这个时候可能想到的第一个解决方案: 使用 synchronized 同步. 代码下修改如下:
在这里插入图片描述

现在问题的确是解决了, 因为同步有了锁. 但是这个代码出现了很明显的性能瓶颈, 因为如果在高并发的访问状态下, 本操作就直接导致系统性能下降.
那么这个时候就必须依靠 volatile 来解决此类问题(该关键字不仅仅是一个同步的问题)
在这里插入图片描述

volatile 关键字的区别:

  • 如果现在没有使用 volatile 关键字定义的变量, 则操作的时候使用的是副本进行的处理, 副本和原始数据之间的同步就需要产生延迟, 而所谓的线程不同步也在于此.
  • volatile 关键字定义的变量表示将直接使用原始数据进行处理, 更改后立即生效.

如果要保证性能高, 则getInstance()方法上就不能使用 synchronized 同步. 修改如下:

package com.beyond.dhl;


class Singleton {
    
    
    private volatile static Singleton instance;   // 懒汉式所以不会进行实例化对象

    private Singleton() {
    
    
        System.out.println("构造方法:" + Thread.currentThread().getName());
    }

    public static Singleton getInstance() {
    
    
        if (instance == null) {
    
    
            synchronized (Singleton.class){
    
    
                if (instance == null){
    
    
                    instance = new Singleton();
                }
            }

        }
        return instance;
    }
}

public class Test {
    
    
    public static void main(String[] args) {
    
    
        new Thread(() -> Singleton.getInstance(), "线程A").start();
        new Thread(() -> Singleton.getInstance(), "线程B").start();
        new Thread(() -> Singleton.getInstance(), "线程C").start();
        new Thread(() -> Singleton.getInstance(), "线程D").start();
        new Thread(() -> Singleton.getInstance(), "线程E").start();
    }
}

猜你喜欢

转载自blog.csdn.net/Beyond_Nothing/article/details/113575782