volatile三大特性

volatile三大特性

1.对volatile的理解

1.volatile是Java虚拟机提供的轻量级的同步机制(同步:synchronized)乞丐版的synchronized。
有三大特性:保证可见性
		  不保证原子性
		  禁止指令重排
可见性:

多个线程,访问java主内存中的同一对象,获取对象之后,各自都拷贝到自己的线程内存中,当有一个线程中的对象改变时,需要写回给主内存中,主内存就会通知其他线程此对象改变了,需要重新拷贝的过程就叫可见性

未加volatile关键字
代码演示
package com.hzy;

import java.util.concurrent.TimeUnit;

class MyData{
    int number = 0;
    public void addT060(){
        this.number = 60;
    }
}

/**
 * 验证线程的可见性
 * 1.假如int number = 0 ;number变量之前根本没有添加volatile关键字,没有可见性
 */
public class VolatileDemo {
    public static void main(String[] args) {
        //资源类
        MyData myData = new MyData();
        //第一个线程
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+"\t come in ");
            //暂停一会线程
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //修改内存中数据为60
            myData.addT060();
            System.out.println(Thread.currentThread().getName()+"\t update number value : "+ myData.number);
        },"AAA").start();

        //第二个线程
        while (myData.number == 0){
            //如果number == 0 mian线程就会在这里一直循环,知道number的值不在等于0
        }
        //如果number中的数据不再等于0,就会执行到下面代码
        System.out.println(Thread.currentThread().getName()+"\t mission is over");
    }
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7pvxfvLu-1614656364824)(C:\Users\HS\AppData\Roaming\Typora\typora-user-images\image-20210228125951266.png)]

加入volatile关键字
代码演示
package com.hzy;

import java.util.concurrent.TimeUnit;

class MyData{
    volatile int number = 0;
    public void addT060(){
        this.number = 60;
    }
}

/**
 * 验证线程的可见性
 * 1.假如int number = 0 ;number变量之前根本没有添加volatile关键字,没有可见性
 */
public class VolatileDemo {
    public static void main(String[] args) {
        //资源类
        MyData myData = new MyData();
        //第一个线程
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+"\t come in ");
            //暂停一会线程
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //修改内存中数据为60
            myData.addT060();
            System.out.println(Thread.currentThread().getName()+"\t update number value : "+ myData.number);
        },"AAA").start();

        //第二个线程
        while (myData.number == 0){
            //如果number == 0 mian线程就会在这里一直循环,知道number的值不在等于0
        }
        //如果number中的数据不再等于0,就会执行到下面代码
        System.out.println(Thread.currentThread().getName()+"\t mission is over,main get number value: " + myData.number);
    }
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BaavQ7w6-1614656364827)(C:\Users\HS\AppData\Roaming\Typora\typora-user-images\image-20210228130305136.png)]

不保证原子性:

为什么没有保证原子性:

在写回主内存中时,多个线程写修改了线程内部变量值后,写回主内存,发生了重复写值现象,没有区分前后顺序,造成了原子性缺失

代码演示
package com.hzy;

import java.util.concurrent.TimeUnit;

class MyData1{
    volatile int number = 0;

    public void addPlusPlus(){
        number++;
    }
}

/**
 * 验证线程的不保证原子性
 * 1.原子性:完整性,不可分割,即某个先后才能正在做某个具体业务时,中间不可加塞或者不可分割,需要整体完整
 *      要么同时成功,要么同时失败
 */
public class VolatileDemo2 {
    public static void main(String[] args) {
        MyData1 myData1 = new MyData1();
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 1; j <= 1000; j++) {
                    myData1.addPlusPlus();
                }
            },String.valueOf(i)).start();
        }
        //需要等待上面20个线程都全部计算完成后,再用main线程取得最终的结果值
        while(Thread.activeCount() > 2){
            //Thread.yield() 方法,使当前线程由执行状态,变成为就绪状态,让出cpu时间,在下一个线程执行时候,此线程有可能被执行,也有可能没有被执行。
            Thread.yield();
        }
        //mian线程的最终值
        System.out.println(Thread.currentThread().getName() + "\t finally number value :" + myData1.number);
    }
}


如果保持了原子性,最终值应该是20000,实际每次得到的值都不相同

如果需要保证原子性,代码如下,在计算方法上加sychronized关键字

package com.hzy;

import java.util.concurrent.TimeUnit;

class MyData1{
    volatile int number = 0;

    public synchronized void addPlusPlus(){
        number++;
    }
}

/**
 * 验证线程的不保证原子性
 * 1.原子性:完整性,不可分割,即某个先后才能正在做某个具体业务时,中间不可加塞或者不可分割,需要整体完整
 *      要么同时成功,要么同时失败
 */
public class VolatileDemo2 {
    public static void main(String[] args) {
        MyData1 myData1 = new MyData1();
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 1; j <= 1000; j++) {
                    myData1.addPlusPlus();
                }
            },String.valueOf(i)).start();
        }
        //需要等待上面20个线程都全部计算完成后,再用main线程取得最终的结果值
        while(Thread.activeCount() > 2){
            //Thread.yield() 方法,使当前线程由执行状态,变成为就绪状态,让出cpu时间,在下一个线程执行时候,此线程有可能被执行,也有可能没有被执行。
            Thread.yield();
        }
        //mian线程的最终值
        System.out.println(Thread.currentThread().getName() + "\t finally number value :" + myData1.number);
    }
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-toL58O0B-1614656364830)(C:\Users\HS\AppData\Roaming\Typora\typora-user-images\image-20210228132625648.png)]

解决不保证原子性问题:

1,在执行运算的方法中加syhronized关键字

2.使用JUC下的atomic,

代码示例:
package com.hzy;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
class MyData1 {
    //AtomicInteger 默认值为 0
    AtomicInteger atomicInteger = new AtomicInteger();
    public void addAtomic(){
        //源码中:加1
        atomicInteger.getAndIncrement();
    }
}
/**
 * 验证线程的不保证原子性
 * 1.原子性:完整性,不可分割,即某个先后才能正在做某个具体业务时,中间不可加塞或者不可分割,需要整体完整
 *      要么同时成功,要么同时失败
 */
public class VolatileDemo2 {
    public static void main(String[] args) {
        MyData1 myData1 = new MyData1();
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 1; j <= 1000; j++) {
                    myData1.addAtomic();
                }
            },String.valueOf(i)).start();
        }
        //需要等待上面20个线程都全部计算完成后,再用main线程取得最终的结果值
        while(Thread.activeCount() > 2){
            //Thread.yield() 方法,使当前线程由执行状态,变成为就绪状态,让出cpu时间,在下一个线程执行时候,此线程有可能被执行,也有可能没有被执行。
            Thread.yield();
        }
        //mian线程的最终值
        System.out.println(Thread.currentThread().getName() + "\t AtomicInteger finally number value :" + myData1.atomicInteger);
    }
}

JMM(java内存模型)一种抽象的规范或规则,并不真正存在

可见性,原子性,有序性

禁止指令重排:

在代码执行时,是编译为字节码指令来提供给进程来执行,在多线程环境下,线程抢到的执行顺序可能会有所不同,会造成数据混乱问题,这个时候数据的安全性机会收到威胁,所以就需要禁止指令重排。保证了多线程环境下不会出现乱序执行的现象

使用:在执行的变量前加volatile

禁止指令重排的好处如下,代码示例:

单线程-单例模式

package com.audition.volatiles;

/**
 * Created with IntelliJ IDEA.
 *
 * @Auther: 两杯水
 * @Date: 2021/02/28/17:15
 * @Description: 单线程-单例模式
 */
public class VolatileDemo3 {
    private static VolatileDemo3 instance= null;

    private VolatileDemo3(){
        System.out.println(Thread.currentThread().getName() +"\t 我是构造方法 ");
    }

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

    public static void main(String[] args) {
        //main线程的操作动作
        System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance());
        System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance());
        System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance());
    }
}

执行结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eMHAx9mv-1614656364834)(C:\Users\86178\AppData\Roaming\Typora\typora-user-images\image-20210228172728840.png)]

进行多线程时,就会有问题,代码如下:

package com.audition.volatiles;

/**
 * Created with IntelliJ IDEA.
 *
 * @Auther: 两杯水
 * @Date: 2021/02/28/17:15
 * @Description: 单线程-单例模式
 */
public class VolatileDemo3 {
    private static VolatileDemo3 instance= null;

    private VolatileDemo3(){
        System.out.println(Thread.currentThread().getName() +"\t 我是构造方法 ");
    }

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

    public static void main(String[] args) {
       /* //main线程的操作动作
        System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance());
        System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance());
        System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance());*/

        //多线程
        for (int i = 0; i <= 10; i++) {
            new Thread(()->{
                VolatileDemo3.getInstance();
            },String.valueOf(i)).start();

        }
    }
}

执行结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MhYAi8QI-1614656364836)(C:\Users\86178\AppData\Roaming\Typora\typora-user-images\image-20210228172815095.png)]

得到结果:多线程下的单例模式,由于多个线程执行的时间不同,导致了构造方法多次被创建实例,也就破坏了原来的单例思想

解决方法

1.在执行方法上加synchronized关键字:

package com.audition.volatiles;

/**
 * Created with IntelliJ IDEA.
 *
 * @Auther: 两杯水
 * @Date: 2021/02/28/17:15
 * @Description: 单线程-单例模式
 */
public class VolatileDemo3 {
    private static VolatileDemo3 instance= null;

    private VolatileDemo3(){
        System.out.println(Thread.currentThread().getName() +"\t 我是构造方法 ");
    }

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

    public static void main(String[] args) {
       /* //main线程的操作动作
        System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance());
        System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance());
        System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance());*/

        //多线程
        for (int i = 0; i <= 10; i++) {
            new Thread(()->{
                VolatileDemo3.getInstance();
            },String.valueOf(i)).start();

        }
    }
}

但是synchronized属于重量级锁,此加锁方式会造成性能降低

2.使用DCL(Double check Lock 双端检索机制)

在这里插入图片描述

但是由于指令重排序的原因,在线程很多并发的情况下,此方法也会导致对象重复获取,所以需要对此对象加上volatile

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值