JUC多线程及并发笔记--volatile问题详解

12 篇文章 0 订阅

1.volatile是java虚拟机提供的轻量级的同步机制

  • 保证可见性
package register;

import java.util.concurrent.TimeUnit;
class MyData{
    volatile int number=0;

    public void addTo60(){
        this.number=60;
    }
}

/**
 * 1.验证volatile可见性
 * 1.1 假如int number=0;number变量之前没有添加volatile关键字修饰,没有可见性
 */
public class VolatileDemo {
    public static void main(String[] args) {
        MyData myData = new MyData();
        new Thread( ()  -> {
            try{
                System.out.println(Thread.currentThread().getName()+"\t come in");
                TimeUnit.SECONDS.sleep(3);
            }catch (Exception e){
                e.printStackTrace();;
            }
            myData.addTo60();
            System.out.println(Thread.currentThread().getName()+"\t update value :"+myData.number);
        },"AAA").start();

        //第二个线程是main函数
        while (myData.number==0){

        }
        //没有volatile修饰,线程卡死,AAA线程修改了number!=0,main线程不可见,一直循环
        System.out.println(Thread.currentThread().getName()+"\t cmiss is over");
    }
}
  • 不保证原子性
package register;
import java.util.concurrent.TimeUnit;

class MyData{
    volatile int number=0;

    public void addTo60(){
        this.number=60;
    }

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

/**
 * 1.验证volatile可见性
 * 1.1 假如int number=0;number变量之前没有添加volatile关键字修饰,没有可见性
 * 1.2 添加volatile可以解决可见性问题
 *
 * 2.验证volatile不保证原子性
 * 2.1 原子性是什么意思?
 *      不可分割,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或者分割。需要整体完整。
 *      要么同时成功,要么同时失败
 */
public class VolatileDemo {
    public static void main(String[] args) {
        MyData myData = new MyData();
        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int j = 0; j <1000 ; j++) {
                    myData.addPublsPubls();
                }
            },String.valueOf(i)).start();
        }
        //需要等待上面20个线程全部计算完成,再用main线程取得最终的结果值看是多少
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println("final value is:\t"+myData.number);
    }
}

造成少于20000原因

如何解决

package register;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class MyData{
    volatile int number=0;

    public void addTo60(){
        this.number=60;
    }

    public  void addPublsPubls(){
        number++;
    }
    //解决volatile不保证原子性问题
    AtomicInteger atomicInteger = new AtomicInteger();

    public void addMyAtomic(){
        atomicInteger.getAndIncrement();
    }
}

/**
 * 1.验证volatile可见性
 * 1.1 假如int number=0;number变量之前没有添加volatile关键字修饰,没有可见性
 * 1.2 添加volatile可以解决可见性问题
 *
 * 2.验证volatile不保证原子性
 * 2.1 原子性是什么意思?
 *      不可分割,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或者分割。需要整体完整。
 *      要么同时成功,要么同时失败
 * 2.2 why
 *
 * 3.3 怎么解决 JUC
 */
public class VolatileDemo {
    public static void main(String[] args) {
        MyData myData = new MyData();
        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int j = 0; j <1000 ; j++) {
                    myData.addPublsPubls();
                    myData.addMyAtomic();
                }
            },String.valueOf(i)).start();
        }
        //需要等待上面20个线程全部计算完成,再用main线程取得最终的结果值看是多少
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println("final value is:\t"+myData.number);
        System.out.println("Atomic final value is:\t"+myData.atomicInteger);
    }
}
  • 禁止指令重拍

2.谈谈JMM

  • JMM--java内存模型

JMM(Java内存模型Java Memory Model,简称JMM)本身是一种抽象的概念并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。

JMM关于同步的规定:

1).线程解锁前,必须把共享变量的值刷新回主内存

2).线程加锁前,必须读取主内存的最新值到自己的工作内存

3).加锁解锁是同一把锁

由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),工.作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,各个线程中的工作内存中存储着主内存中的变量副本拷贝,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,其简要访问过程如下图:

  • JMM--特性

(1)可见性

t1线程修改了主物理内存的值,马上通知其他线程,其他线程取的值作废,重新从内存中获取最新值。

(2)原子性

(3)有序性

3.你在那些地方用到过volatile

3.1单例模式DCL代码

package register;

public class SingletonDemo {
    private  static SingletonDemo instance=null;
    private SingletonDemo(){
        System.out.println(Thread.currentThread().getName()+"\t 我是构造方法SingletonDemo()");
    }

    //多线程模式下可以在方法上使用synchronized进行修饰,可以保证单利模式下只创建一个实例。
    // 但是synchronized会锁住整个方法,使效率变低
    public  static SingletonDemo getInstance(){
        if(instance==null){
            instance=new SingletonDemo();
        }
        return instance;
    }

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

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

 DCL模式:Double check lock双端检索机制,在获取单例方法内使用synchronized代码块及双null判断,DCL机制不一定线程安全,原因是会存在有指令重排的存在,加入volatile可以进制指令重排

public  static SingletonDemo getInstance(){//方法
    if(instance==null){
        synchronized (SingletonDemo.class){
            if(instance==null){
                instance=new SingletonDemo();
            }
        }
    }
    return instance;
}

3.2 单例模式volatile分析

package register;

public class SingletonDemo {
    //使用volatile修饰,可以有效解决指令重排造成多线程下单利模式获得多个实例被创建
    private volatile   static SingletonDemo instance=null;
    private SingletonDemo(){
        System.out.println(Thread.currentThread().getName()+"\t 我是构造方法SingletonDemo()");
    }
    public  static SingletonDemo getInstance(){
        if(instance==null){
            synchronized (SingletonDemo.class){
                if(instance==null){
                    instance=new SingletonDemo();
                }
            }
        }
        return instance;
    }

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

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

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值