zoukankan      html  css  js  c++  java
  • java 语言多线程可见性(synchronized 和 volatile 学习)

    共享变量可见性实现的原理
    java 语言层面支持的可见性实现方式: synchronized volatile


    1、 synchronized 的两条规定:
    1 线程解锁前,必须把共享变量的最新值刷新到主内存中。
    2 线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(加锁与解锁需要是同一锁)
    线程解锁前对共享变量的修改在下次加锁时对其他线程可见。

    2、 volatile 实现可见性
    深入来说,通过加入内存屏障禁止重排序优化来实现 的。
    对volatile变量执行写操作时,会在写操作后加入一条store屏障指令。
    对volatile变量执行读操作时,会在读操作前加入一条load 屏障指令。
    通俗地讲,volatile 变量在每次被线程访问时,都强迫从主内存中重读该变量的值,而当该变量发生变化时,又会
    强迫线程将最新的值刷新到主内存中,这样任何时刻,不同的线程总能看到该变量的最新值 。
    但volatile 不能保证变量的原子性。

    典型的num++ 操作,使用volatile修饰变量时,无法保存操作的原子性,所以会出现很多值的情况。
    如果出现这种情况,还需要使用volatile时解决办法就是:

    使用synchronized 关键字 进行 包装。ReentrantLock 进行代码前的加锁,然后使用unlock 释放锁。

    在使用volatile 的时候注意专场合:

    • 1、对变量的写入操作不依赖其当前的值。典型的就是numb++ num=num*3 等 。
    • 2、该变量没有包含在具有其他变量的不变式中。(例如两个volatile 变量 存在大小关系中,或其他关系中等)

    3、 synchronized 和 volatile 比较

    • 1、volatile 不需要加锁,比synchronized 更经量级,不会阻塞线程。
    • 2、从内存可见性角度讲,volatile 读相当于加锁,volatile 写相当于解锁。
    • 3、synchronized 即能保证可见性,又能保证原子性,而volatile 只能保证可见性,无法保证原子性。

    代码示例:

    package org.apple.thread;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class VolatileTest {
        
        private Lock lock = new ReentrantLock();
        
        private volatile int number = 0;
        
        public void increase(){
              this.number++;
            // 第二种解决办法 
    //        lock.lock();
    //        this.number++;
    //        lock.unlock();
            // 第一种解决办法
            //synchronized(this){
            //    this.number++;
            //}
        }
        
        public int getNumber(){
            
            return this.number;
        }
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            final VolatileTest volatileTest = new VolatileTest();
            for (int i = 0; i < 500; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        volatileTest.increase();
                    }
                }).start();
            }
            // 如果当前线程还有子线程在运行,主线程则让出cpu 资源,直到所有的子线程运行完成后,则往下继续执行。
            if(Thread.activeCount()>1){
                Thread.yield();
            }
            System.out.println(Thread.activeCount());
            System.out.println(volatileTest.getNumber());
        }
    
    }
  • 相关阅读:
    vue项目中兼容ie8以上浏览器的配置
    vue项目中event bus的简单使用
    js中的正则表达式小结1
    sourceTree跳过注册
    日期时间格式化
    阿里矢量图标库的使用
    mySql 常用命令
    php 常用的系统函数
    php mySql常用的函数
    php 关于php创建 json文件 和 对文件增删改查 示例
  • 原文地址:https://www.cnblogs.com/shaoyu19900421/p/4405092.html
Copyright © 2011-2022 走看看