zoukankan      html  css  js  c++  java
  • 细说Java多线程之内存可见性

    synchronized实现的可见性

    JMM (java memory model) 关于synchronized的两条规定

    线程解锁前,必须把共享变量的最新值刷新到主内存中

    线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时,需要从主内存中重新读取最新的值(注意,加锁与解锁需要是同一把锁

    线程解锁前对共享变量的修改在下次加锁是对其他线程可见

    线程执行互斥代码的过程

    1.获得互斥锁

    2.清空工作内存

    3.从主内存拷贝变量的最新副本到工作内存

    4.执行代码

    5.将更改后的共享变量的值刷新到主内存

    6.释放互斥锁


    重排序

    代码书写的顺序与实际执行的顺序不同,指令重排序是编译器或处理器为了提高程序性能而做的优化

    1.编译器优化的重排序(编译器优化)

    2.指令集并行重排序 (处理器优化)

    3.内存系统的重排序 (处理器优化)


    as-if-serial

    无论如何重排序,程序执行的结果应该与代码顺序执行的结果一致(Java编译器、运行时和处理器都会保证Java在单线程下遵循as-if-serial 语义)


    导致共享变量在线程间不可见的原因                     synchronized解决方案:

    1.线程的交叉执行                                                                 ---------------->原子性

    2.重拍结合线程交叉执行                                                     ---------------->原子性

    3.共享变量更新后的值没有在工作内存与主内存间及时更新         ---->可见性



    volatile 实现可见性

    volatile 关键字

    能够保证volatile变量的可见性

    不能保证volatile变量符合操作的原子性

    volatile如何实现内存可见性:

    深入来说:通过加入内存屏障和禁止重排序优化来实现的。

    对volatile 变量执行写操作时,会在写操作后加入一条store屏障指令

    对volatile变量执行读操作时,会在读操作前加入一条load屏障指令

    通俗地讲:volatile变量发生变化时,又会强迫线程将最新的值刷新到主内存。这样任何时刻,不同的线程总能看到该变量的最新值

    线程写 volatile变量的过程

    1.改变线程工作内存中volatile变量副本的值

    2.将改变后的副本的值从工作内存中刷新到主内存

    线程读 volatile变量的过程

    1.从主内存中读取volatile变量的最新值到线程的工作内存中

    2.从工作内存中读取volatile变量的副本

    volatile 不能保证volatile 变量渡河操作的原子性



    public class VolatileDemo {


    volatile int number = 0;


    public int getNumber(){
    return this.number;
    }
    public void increase(){
    try {
    Thread.sleep(100);
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    this.number++;
    }


    public static void main(String[] args) {

    VolatileDemo volatileDemo = new VolatileDemo();
    for(int i=0;i<500;i++){
    new Thread(new Runnable() {

    @Override
    public void run() {
    // TODO Auto-generated method stub
    volatileDemo.increase();
    }
    }).start();
    }
    //如果还有子线程在运行,主线程就让出CPU资源
    //知道所有的子线程都运行完了,在运行主线程继续往下执行
    while(Thread.activeCount()>1){
    Thread.yield();
    }
    System.out.println("number:"+volatileDemo.getNumber());



    }


    }



    解决方案一

      int number = 0; 去掉volatile

    public int getNumber(){
    return this.number;
    }
    public  void increase(){
    try {
    Thread.sleep(100);
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    synchronized(this){
    this.number++;
    }

    }

    解决方案二

    用ReentrantLock实现number变量在线程中的原子性

                    Lock lock = new ReentrantLock();

    //加锁和开锁块,就相当于synchronized
    lock.lock();

    try {
    this.number++;
    } finally  {
    lock.unlock();
    }

  • 相关阅读:
    mac os programming
    Rejecting Good Engineers?
    Do Undergrads in MIT Struggle to Obtain Good Grades?
    Go to industry?
    LaTex Tricks
    Convert jupyter notebooks to python files
    How to get gradients with respect to the inputs in pytorch
    Uninstall cuda 9.1 and install cuda 8.0
    How to edit codes on the server which runs jupyter notebook using your pc's bwroser
    Leetcode No.94 Binary Tree Inorder Traversal二叉树中序遍历(c++实现)
  • 原文地址:https://www.cnblogs.com/CCCrunner/p/6444551.html
Copyright © 2011-2022 走看看