java并发编程之volatile关键字

1、volatile的作用

  一个线程共享变量(类的成员变量、类的静态成员变量等)被volatile修饰之后,就具有以下作用:

  1)并发中的变量可见性(不同线程对该变量进行操作时的可见性),即一个线程修改了某个变量的值,则该新值对其他线程立即可见(可立即访问新值/立即强制写入主存);

  2)禁止指令重排(包括java编译器和CPU运行时指令重排序);

3)禁用缓存(java虚拟机规范)---子线程的工作内存(包括了CPU缓存)。

2、相关概念

2.1)指令重排序:

(1)java编译器运行时指令重排序(实质是JVM的优化处理),java源码编译生成class文件后,JVM需要在运行时(runtime)将字节码文件(class文件)转化为操作系统能够执行的指令(JIT编译器),在转换的过程中jvm会对指令进行优化调整,以提高运行效率。

(2)CPU运行时指令重排序,cpu优化的方式,为避免处理器访问主内存的时间开销,处理器采用缓存机制(三层缓存)提高性能(缓存之间的数据一致性遵循协议规范),当CPU写缓存时,发现缓存区块正被其他CPU占用,为了提高CPU的处理性能,可能将后面的读缓存命令优先执行。

 2.2)java内存模型规范:

线程要操作共享变量时,需要从主内存读取到工作内存,改变值后需要从工作内存同步到主内存中。多线程的情况下,同步到主内存时遵循同步协议规范。

3、相关现象分析

3.1)先看一段代码,

public class VolatileInfo {

	private static boolean flag = true;

	public static void main(String[] args) {
		new Thread(new Runnable() {
			@Override
			public void run() {
				long start = System.currentTimeMillis();
				long end = 0;
				int index = 0;
				while (VolatileInfo.flag) {
					index++;
					end = System.currentTimeMillis();
					if ((end-start)/1000==5) {//5秒之后结束任务
						break;
					}
				}
				System.out.println("index:"+index);
				System.out.println("cost:"+(end-start)/1000+"s");
			}
		}).start();

		try {
			TimeUnit.SECONDS.sleep(2);//阻塞线程2秒
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		VolatileInfo.flag = false;
		System.out.println("条件置为false");
	}
}

  输出结果:

条件置为false
index:269460217
cost:5s

  耗时5秒,并不是2秒后停止(注:静态变量flag用volatile 修饰后执行时间是2秒)。

3.2)问题分析:

1)主线程阻塞后修改的flag的值并没有及时写入到主存,子线程没有及时读取到flag值,导致循环继续执行---即存在缓存;

2)指令重排序,先从主内存中执行读的操作,陷入死循环,不再向主内存写入。

3.3)解决办法:

1)flag变量使用volatile关键词修饰;

  2)使用Synchronized加同步锁(从主存中直接读取,相当于禁用缓存,参考4.3);

	while (VolatileInfo.flag) {
		synchronized (this) {
		   index++;
		}
		end = System.currentTimeMillis();
		if ((end - start) / 1000 == 5) {// 5秒之后结束任务
			break;
		}
	}

3)从eclipse中将jvm执行class文件的方式改为client(默认是server模式,jvm进行了一些优化调整);

4)从eclipse中配置添加关闭server优化的参数---此处请自行百度^_^;              ---------- 禁止指令重排。

4、扩展知识点

4.1)java内存模型多线程情况下的工作内存和主内存同步协议的8种原子操作:

  lock(锁定):作用于主内存,将主内存中的变量锁定,为一个线程所独有;

unlock(解锁):作用于主内存,解除lock的锁定,释放后的变量能够被其他线程访问;

  read(读取):作用于主内存,将主内存中的变量读取到工作内存中,以便随后的load动作使用;

  load(载入):作用于工作内存,它把read读取的值保存到工作内存中的变量副本中;

  use(使用):作用于工作内存,它把工作内存中的值传递给线程代码执行引擎;

  assign(赋值):作用于工作内存,它把从执行引擎处理返回的值重新赋值给工作内存中的变量;

  store(存储):作用于工作内存,将变量副本中的值传送到主内存中,以备随后的write操作使用;

  write(写入):作用于主内存,它把store传送值写到主内存的共享变量中。

4.2)java内存模型操作规范:

  1)将一个变量从主内存复制到工作内存要顺序依次(不一定连续)执行read、load操作;

2)做了assign操作,必须同步回主内存等。

4.3)保证线程共享变量可见性的方式:

  1)用final修饰的变量

2) Synchronized 同步锁

    Synchronized规范, 进入同步块前,先清空工作内存中的共享变量,从主内存中重新加载; 解锁前必须把修改的共享变量同步回主内存。

    锁机制,锁机制保护共享资源,只有获得锁的线程才能操作共享资源。

3) 用volatile修饰的变量

    volatile语义规范,使用volatile修饰的变量时,必须从主内存中加载,并且read、load是连续的;修改volatile修饰的变量时,必须立即同步到主内存,并且store、write是连续的。

一字一句敲的,支持原创,转载请注明出处,谢谢:https://www.cnblogs.com/huyangshu-fs/p/10225898.html

原文地址:https://www.cnblogs.com/huyangshu-fs/p/10225898.html

时间: 2024-11-14 08:55:05

java并发编程之volatile关键字的相关文章

Java并发编程之volatile关键字解析

volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机.volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatile关键字的实现原理,最后给出了几个使用volat

Java并发编程之volatile的理解

Java并发编程之volatile关键字的理解 Java中每个线程都有自己的工作内存,类比于处理器的缓存,线程的工作内存中保存了被该线程使用到的变量的主内存的拷贝.线程读写变量都是直接在自己的工作内存中进行的,而何时刷新数据(指将修改的结果更新到主存或者把主存的变量读取覆盖掉工作内存中的值)是不确定的. volatile关键字是修饰字段的关键字,貌似是JDK1.5之后才有的,在多线程编程中,很大的几率会用到这个关键字,volatile修饰变量后该变量有这么一种效果:线程每一次读该变量都是直接从主

Java并发编程之volatile变量

volatile提供了弱同步机制,用来确保将变量更新通知到其它线程.volatile变量不会被缓存在寄存器中或者对其它处理器不可见的地方,因此在读取volatile变量时总会返回最新写入的值.可以想象成如下语义,然而volatile是更轻量级的同步机制.volatile只能确保可见性,但不能保证原子性.也就是说不能在复合操作用volatile变量,比如i++. public synchronized void setValue(int value){ this.value = value; }

Java并发编程之ConcurrentHashMap

ConcurrentHashMap ConcurrentHashMap是一个线程安全的Hash Table,它的主要功能是提供了一组和HashTable功能相同但是线程安全的方法.ConcurrentHashMap可以做到读取数据不加锁,并且其内部的结构可以让其在进行写操作的时候能够将锁的粒度保持地尽量地小,不用对整个ConcurrentHashMap加锁. ConcurrentHashMap的内部结构 ConcurrentHashMap为了提高本身的并发能力,在内部采用了一个叫做Segment

6、Java并发编程:volatile关键字解析

Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatil

java并发编程之Master-Worker模式

Master-Worker模式适合在一个任务可以拆分成多个小任务来进行的情况下使用. package cn.fcl.masterworker; import java.util.HashMap; import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; public c

java并发编程之future模式

1.当你想并发去执行一段代码,但是还想获取这段代码的返回结果,那么future多线程模式就可以派上用场了,代码实现如下. public class Client { public Data request() { final FutureData futureData = new FutureData(); new Thread(new Runnable() { @Override public void run() { futureData.setRealData(new RealData()

java并发编程之Guarded Suspention

当客户端请求速度远远大于服务端的处理速度,这时候就非常适合使用Guarded Suspention模式 package cn.fcl.guardendSuspension; import java.util.ArrayList; import java.util.List; public class RequestQueue { private List<Integer> integers = new ArrayList<Integer>(); public synchronize

Java并发编程:volatile关键字解析 和双重检查(Double-Check)

转载:http://www.cnblogs.com/dolphin0520/p/3920373.html Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的