• java并发-Volatile关键字


    Volatile关键字最大的特点是,在多线程中保持变量的可见性。这个可见性主要是指变量在主存中的可见性。因此,有必要了解java内存模型。

    java内存模型

    java中每个线程有一块工作内存区,存放着被所有线程共享的主内存中的变量的值的拷贝。一个线程可以执行的操作有:使用(use)、赋值(assign)、装载(load)、存储(store)、锁定(lock)、解锁(unlock)。
    这里写图片描述

    主内存可执行的操作有读、写、锁定、解锁,每个操作都是原子的。一个变量是java程序可以存取的一个地址,它可以是基本类型、引用类型变量等。保存在主内存区的变量可以被所有线程共享,但一个线程存取另一个线程的参数或者局部变量是不可能的。
    其中:使用(use)、赋值(assign)、锁定(lock)、解锁(unlock),这些操作都是原子操作,但是主内存与线程之间的数据传递不具有原子性,例如:当数据从主存中到工作内存时,需要两个动作:
    1:主存执行读(read)操作;
    2:工作内存执行相应的load操作;
    下边我们看看上述各个操作的含义:
    1:use:把一个变量在线程工作内存的拷贝内容传送给线程执行引擎;
    2:assign:把一个值从线程执行引擎传送给变量的线程工作内存;
    3:read:把一个变量的主内存拷贝传送到线程的工作内存,以便线程的load操作进行;
    4:load:把read操作从主内存中得到的值放入线程内存;
    5:store:把一个变量的线程拷贝内容传送到主内存,以便write操作使用;
    6:write:把store操作的变量放入主内存;
    7:主内存的lock操作是线程获得一个独占锁;
    8:主内存的unlock操作是线程释放一个独占锁。

    volatile关键字

    1:volatile关键字的作用:

    • 其它线程对变量的修改,可以及时反应在当前线程上;
    • 确保当前线程对volatile变量的修改,能立即写回主内存,被其它线程所见,即保证变量的可见性;
    • volatile声明的变量,编译器会保证其有序性。
    public class Volatile {
        public volatile static boolean stop=false;
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
            new Thread(new Runnable() {
    			public void run() {
    				Volatile.test();
    			}
    		}).start();
            new Thread(new Runnable() {
    			
    			@Override
    			public void run() {
    				// TODO Auto-generated method stub
    				stop=true;
    			}
    		}).start();
            
    	}
    	public  static void test()
    	{
    		while(!stop)
    		{
    			System.out.println("线程"+Thread.currentThread().getName()+"还在运行");
    		}
    		if(stop)
    		{
    			System.out.println("线程"+Thread.currentThread().getName()+"结束运行");
    		}
    	}
    
    }
    

    2.volatile不保证原子性

    public class Volatile {
    	public volatile int inc = 0;
        
        public void increase() {
            inc++;
        }
         
        public static void main(String[] args) {
            final Volatile test = new Volatile();
            for(int i=0;i<10;i++){
                new Thread(){
                    public void run() {
                        for(int j=0;j<1000;j++)
                            test.increase();
                    };
                }.start();
            }
             
            while(Thread.activeCount()>1)  //保证前面的线程都执行完
                Thread.yield();
            System.out.println(test.inc);
        }
    
    }
    

    运行发现,每次的运行结果不一样,这是由于inc自增不是原子性的。
    可以改变这个现状有两个方法:
    1:采用synchronized:

    public class Test {
        public  int inc = 0;
        
        public synchronized void increase() {
            inc++;
        }
        
        public static void main(String[] args) {
            final Test test = new Test();
            for(int i=0;i<10;i++){
                new Thread(){
                    public void run() {
                        for(int j=0;j<1000;j++)
                            test.increase();
                    };
                }.start();
            }
            
            while(Thread.activeCount()>1)  //保证前面的线程都执行完
                Thread.yield();
            System.out.println(test.inc);
        }
    }
    

    2:Lock

    public class Test {
        public  int inc = 0;
        Lock lock = new ReentrantLock();
        
        public  void increase() {
            lock.lock();
            try {
                inc++;
            } finally{
                lock.unlock();
            }
        }
        
        public static void main(String[] args) {
            final Test test = new Test();
            for(int i=0;i<10;i++){
                new Thread(){
                    public void run() {
                        for(int j=0;j<1000;j++)
                            test.increase();
                    };
                }.start();
            }
            
            while(Thread.activeCount()>1)  //保证前面的线程都执行完
                Thread.yield();
            System.out.println(test.inc);
        }
    }
    

    4.volatile的原理和实现机制
    “观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”

      lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:

      1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;

      2)它会强制将对缓存的修改操作立即写入主存;

      3)如果是写操作,它会导致其他CPU中对应的缓存行无效。

    使用volatile关键字的场景

    synchronized关键字是防止多个线程同时执行一段代码,那么就会很影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized,但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。通常来说,使用volatile必须具备以下2个条件:

     1)对变量的写操作不依赖于当前值
     2)该变量没有包含在具有其他变量的不变式中
    

      实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。
      1.状态标记量
      

    volatile boolean flag = false;
     
    while(!flag){
        doSomething();
    }
     
    public void setFlag() {
        flag = true;
    }
    

    2:volatile 关键字来保证多线程下的单例

    public class Singleton {
        private volatile Singleton instance = null;
        public Singleton getInstance() {
            if (instance == null) {
                synchronized(this) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    
    

    引用块内容
    http://www.cnblogs.com/dolphin0520/p/3920373.html
    java程序性能优化
    java核心技术 卷I

  • 相关阅读:
    面试突击44:volatile 有什么用?
    面试突击42:synchronized和ReentrantLock有什么区别?
    面试突击45:为什么要用读写锁?它有什么优点?
    微信内打开链接,跳转到公众号关注页面
    阿里流水线使用教程
    windows自带 扫描修复系统
    20年前的网站页面
    微服务之 Consul 单机版到集群搭建详细步骤【转】
    iOS修改项目名称
    从零开始学YCFramework之初步
  • 原文地址:https://www.cnblogs.com/csuwater/p/5411116.html
Copyright © 2020-2023  润新知