JMM&并发三大特性

JMM简介

Java Memory Mode,java内存模型,java多线程通信模型
涉及到维度:JAVA 层面 JVM层面 硬件层面

需要重点掌握理解 并发的三大特性,JMM工作内存和主存的关系,多线程是如何通信的,volatile保证可见性和有序性,CAS

并发和并行

并行:同一时刻,多条指令可以在多个处理器同时执行
并发:同一时刻,只有一条指令执行。多个线程之间快速轮动,交替执行

并发的三大特性

可见性 一个线程改变了共享变量的值,其他线程可以看到修改后的值

如何保证:
通过volatile关键字保证可见性
通过内存屏障保证可见性
通过synchronized关键字保证可见性
通过Lock保证可见性
通过final关键字保证可见性
还可以通过是内存中关键字失效来保证

有序性 程序执行顺序按照代码先后顺序执行,JVM存在指令重排,所以存在有序性问题

如何保证:
通过volatile关键字保证有序性
通过内存屏障保证有序性
通过synchronized关键字保证有序性
通过Locak保证有序性

原子性 一个或多个操作,要么全执行要不全部不执行

如何保证:
通过synchronized关键字保证原子性
通过Lock保证原子性
通过CAS保证原子性

并发编程的bug来源就来着可见性,有序性,原子性。

可见性

代码示例:

package com.example.demo.thread;


/**
 * @author Fox
 *
 * -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp
 * hsdis-amd64.dll
 *  可见性案例
 *
 *  1.采用volatile修饰
 *  2. int采用包装类Integer
 *  3.采用内存屏障
 * 4.synchronized()
 */
public class VisibilityTest {
    //  storeLoad  JVM内存屏障  ---->  (汇编层面指令)  lock; addl $0,0(%%rsp)
    // lock前缀指令不是内存屏障的指令,但是有内存屏障的效果   缓存失效
    private  boolean flag = true;   //volatile
    private int count = 0;     //Integer

    public void refresh() {
        flag = false;
        System.out.println(Thread.currentThread().getName() + "修改flag:"+flag);
    }

    public void load() {
        System.out.println(Thread.currentThread().getName() + "开始执行.....");
        while (flag) {
            //TODO  业务逻辑
            count++;
            //JMM模型    内存模型: 线程间通信有关   共享内存模型
            //没有跳出循环   可见性的问题
            //能够跳出循环   内存屏障
            //UnsafeFactory.getUnsafe().storeFence();
            //能够跳出循环    ?   释放时间片,上下文切换   加载上下文:flag=true
            //Thread.yield();
            //能够跳出循环    内存屏障
            //System.out.println(count);

            //LockSupport.unpark(Thread.currentThread());

            //shortWait(1000000); //1ms
            //shortWait(1000);
//            try {
//                Thread.sleep(1);   //内存屏障
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
            //总结:  Java中可见性如何保证? 方式归类有两种:
            //1.  jvm层面 storeLoad内存屏障    ===>  x86   lock替代了mfence
            // 2.  上下文切换   Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + "跳出循环: count=" + count);
    }

    public static void main(String[] args) throws InterruptedException {
        VisibilityTest test = new VisibilityTest();

        // 线程threadA模拟数据加载场景
        Thread threadA = new Thread(() -> test.load(), "threadA");
        threadA.start();

        // 让threadA执行一会儿
        Thread.sleep(1000);
        // 线程threadB通过flag控制threadA的执行时间
        Thread threadB = new Thread(() -> test.refresh(), "threadB");
        threadB.start();

    }


    public static void shortWait(long interval) {
        long start = System.nanoTime();
        long end;
        do {
            end = System.nanoTime();
        } while (start + interval >= end);
    }
}

以上列举的好多方法,都能保证可见性。最常使用的是采用volatile关键字修饰,volatile不具有原子性。

当写一个volatile变量时,JMM会把该线程对应的本地内存中共享变量刷新到主内存中
当读一个volatile变量时,JMM会把该线程对应的本地内存中共享变量置为无效,去
主内存中获取共享变量

在这里插入图片描述

有序性

示例代码如下

package com.example.demo.jmm;

public class ReOrderTest {

    private  static int x=0,y=0;
    private      static int a=0,b=0;

    public static void main(String[] args) throws InterruptedException {
        int i=0;
        while (true){
            i++;
            x=0;
            y=0;
            a=0;
            b=0;
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                  shortWait(20000);
                  a=1;
                  x=b;
                }
            });
            Thread thread2= new Thread(new Runnable() {
                @Override
                public void run() {
                    shortWait(20000);
                    b=1;
                    y=a;
                }
            });

            thread1.start();
            thread2.start();
            thread1.join();
            thread2.join();
            System.out.println("第" + i + "次" + "x=" + x + ",y="+ y);
            if(x==0&& y==0){
                break;
            }
        }


    }
    public  static  void  shortWait(long intval){
        long start = System.currentTimeMillis();
        long end;
        do{
            end = System.nanoTime();
        }while (start + intval >=end);
    }
}

执行多次,x,y的值不是固定的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值