volatile关键字和单例模式

本文探讨了volatile关键字的作用,包括保证共享变量的可见性和部分顺序性,但不保证原子性。接着,文章分析了单例模式,指出类加载过程确保线程安全,并讨论了指令重排序对单例模式的影响。最后,列举了饿汉模式和懒汉模式的实现,强调了在懒汉模式中使用volatile的重要性,以确保多线程环境下的安全性。
摘要由CSDN通过智能技术生成

volatile

1、作用

(1)修饰的共享变量

(2)保证可见性,变量都是从主存进行操作;

(3)部分保证顺序性,禁止指令重排序,建立内存屏障

2、注意事项:不保证原子性

3、结论

如果一个变量的操作,不会分解为多条指令,就是线程安全的;否则就是线程不安全的

(1)依赖某个共享变量的值进行修改操作,就会分解
(2)n++,n–,++n,–n,!flag,new对象是非线程安全的

单例模式

1、单例:在某些场景下,需要获取同一个对象

2、类加载在JVM内部执行,保证了线程间同步互斥的:

(1)只执行一次
(2)线程安全

3、指令重排序:

(1)JVM编译java文件为class字节码文件时、CPU执行机器码(JVM运行时解释字节码为字节码)时,可以进行指令重排序

(2)是否可以进行重排序,依赖指令前后依赖关系

int i = 9;
int j = i;//存在依赖关系,不能重排序
int m = 9;//不存在依赖关系,可以重排序

1、饿汉模式

代码:

public class SingleTest {
    //饿汉模式:在类加载的时候就创建对象
    public static SingleTest SINGLE = new SingleTest();

    public SingleTest() {}
    
    public static SingleTest getInstance(){
        return SINGLE;
    }
}

2、懒汉模式

(1)线程不安全的版本

public class SingleTest{
    //懒汉模式
    //存在线程安全问题
    public static SingleTest SINGLE = null;

    public SingleTest() {}

    public static SingleTest getInstance() {
        if(SINGLE == null){
            SINGLE = new SingleTest();
            /*
            SINGLEA = new SingleTest()可以分解为:
            (1)分配对象的内存空间
            (2)初始化对象
            (3)对象赋值给变量
            不具有原子性,可见性
             */
        }
        return SINGLE;
    }
}

(2)线程安全,但效率低的版本

public	class SingleTest{
 //懒汉式多线程安全版:存在效率低的问题
    public static SingleTest SINGLE;

    public SingleTest() {}

    //对方法加锁
    public synchronized static SingleTest getInstance() {
        if (SINGLE == null) {
            SINGLE = new Sington();
        }
        return SINGLE;
    }
}

(3)线程安全,且效率高

<1>没有使用volatile,此时只能保证第一个线程的安全性,不能保证后面线程的安全性

public	class SingleTest{
 //懒汉式多线程安全版,且效率高
    public static SingleTest SINGLE;

    public SingleTest() {}

    //对方法加锁,二次判断,性能高
    public synchronized static SingleTest getInstance() {
        if (SINGLE == null) {
            synchronized (Sington.class) {
                if (SINGLE == null) {
                    SINGLE = new SingleTest();
                    //(1)分配对象的内存空间
                    //(2)初始化对象
                    //(3)对象赋值给变量
                }
            }
        }
        return SINGLE;
    }
}

SINGLE = new SingleTest();可以分解为(1)分配对象的内存空间;(2)初始化对象;(3)对象赋值给变量;

在第1个线程执行时,(3)可以重排序到(2)之前;此时第二个线程在执行时,SINGLE有指向的对象,但是对象没有初始化,就会出错

<2>使用volatile关键字,可以保证所有线程的安全性

public class VolatileSingle {
    //双重校验锁的单例模式
    public volatile static VolatileTest SAFESINGTON;
    //volatile的使用保证了变量使用前后的顺序性
    public static VolatileTest getInstance(){
        //多个线程进入代码块,按序执行
        if(SAFESINGTON == null){
            synchronized (VolatileTest.class){
                //第一个进入的线程初始化对象,后序进入的直接退出
                //后续的线程并发的执行getInstance(),不会进入if代码,直接返回已实例的对象
                //效率非常高
                if(SAFESINGTON == null){
                    SAFESINGTON = new VolatileTest();
                    /*
                    SAFESINGTON = new VolatileTest()分解为
                    (1)分配对象的内存空间
                    (2)初始化对象
                    (3)对象赋值给变量
                     */
                }
            }
        }
        return SAFESINGTON;
    }
}

对于重排序:

1、 一个线程看自己内部的代码,都是有序的:虽然还是会重排序,但是前后依赖是可以保证的,可以得到期望的结果

2、从某个线程的角度,看其他线程内代码执行的顺序,都是无序的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值