博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
volatile 修饰数组
阅读量:6710 次
发布时间:2019-06-25

本文共 6617 字,大约阅读时间需要 22 分钟。

hot3.png

volatile Array is a volatile reference to an array, not a reference to an array of volatile elements! 

I get asked a lot about how the volatile keyword interacts with arrays, so it is probably worth a blog post on the subject.

Those of you who have read my posts on volatile ( and, most importantly, ) will have a pretty good idea of what volatile means, but it is probably worth it to provide a reminder. 
Basically, if you write to a volatile field, and then you have a later read that sees that write, then the actions that happened before that write are guaranteed to be ordered before and visible to the actions that happen after the read. In practice, what this means is that the compiler and the processor can't do any sneaky reordering to move actions that come before the write to after it, or actions that come after the write to before it. See my post on  for more detail.
With that out of the way, let's go through some examples of what you can do with volatile arrays:

volatile int [] arr = new int[SIZE];arr = arr;int x = arr[0];arr[0] = 1;
The first lesson to learn, which will guide us here, is that 
arr is a volatile reference to an array, not a reference to an array of volatile elements!
 As a result, the write to arr[0]
 is not a volatile write. If you write to arr[0]
, you will get none of the ordering or visibility guarantees that you want from a volatile write.
What examples are there of a volatile write in the code above? Well, both of the writes to arr — the self-assignment and the write of new int[SIZE]
 — are volatile writes, because they are writing to arr
, not one of its elements.
That explains where the volatile writes are. Where are the volatile reads in our example? It turns out that each of the lines after the declaration contains a volatile read:
arr = arr
This one is easy. The volatile read is on the right hand side of the assignment statement.
int x = arr[0];
This one is slightly more subtle. The volatile read is 
not
 the read of the array element. The right hand side of that assignment is a two step process. First, you read the array reference, then you read the 0th element of that array. The volatile read is the read of the array reference, 
not
 the read of the 0th element.
arr[0] = 1;
The previous example should give you a hint of where the volatile read is on this line. As in that example, the left-hand side is a two step process. First, you read the array reference, then you assign to the 0th element of that array. As odd as it seems, 
the read of the array reference is a volatile read
.
The astute reader will notice that there is no actual way to get volatile write semantics by writing to an element of an array referenced by a volatile field. The easiest way to get volatile array semantics is to use the Atomic[Reference/Long/Integer]Array
 classes in java.util.concurrent.atomic
, which provide volatile semantics for reads and writes of individual elements.
(Why not Float/Short/Double Array? With APIs, you never ask "why not", you ask "why". Meanwhile, you have 32- and 64-bit bit patterns, so the Float.floatToIntBits
 and Float.intBitsToFloat
 family of functions are your friends.)
These classes are somewhat problematic, though. 
If nothing else, you are endlessly boxing and unboxing values, which may make access expensive.
 
Ugh — I really do know better than this, really!
 As a result, there is more to this story.
You may have noticed that I did provide a way of getting a volatile write above with just arrays: by writing out a self-reference. I have been asked if that technique can be leveraged to provide volatile access to array elements. Here's what that would look like: 
// I wouldn't do this if I were you.volatile int [] arr = new int[SIZE];arr[0] = 1;arr = arr;
This definitely does provide the volatile write. However, what good does it do you? The virtue of a volatile write is that a corresponding read can detect that it happened, and do something based on the new value. For example, you can use a volatile flag to force one thread to loop indefinitely until another one sets the flag. In this case, you can't actually detect that another thread performed the write, because it is writing the same value to the variable.
You can use sun.misc.Unsafe
 to emulate the behavior of a volatile array. But not everyone is working on a Sun VM. And they are trying to discourage the adoption of sun.misc.Unsafe, so I'm not going to put the code here.

Don't despair too much, though — Doug Lea and the clever folks involved in JSR-166 are working on better solutions for Java 7. More news as events warrant.

http://www.javamex.com/tutorials/volatile_arrays.shtml

Volatile arrays in Java

A slight complication of Java volatile fields, and one sometimes overlooked, is that declaring an array volatile does not give volatile access to its fields!. At least, it doesn't when elements of the array are accessed with "normal" Java syntax. In other words:

  • it is unsafe to call arr[x] = y on an array (even if declared volatile) in one thread and then expect arr[x] to return y from another thread;
  • on the other hand, it is safe to call arr = new int[] (or whatever) and expect another thread to then read the new array as that referenced by arr: this is what is meant by declaring the array reference volatile.

So, what do we do if we want a truly volatile array in Java, where the array's individual fields have volatile semantics?

Solution 1: use AtomicIntegerArray or AtomicLongArray

The AtomicIntegerArray class implements an int array whose individual fields can be accessed with volatile semantics, via the class's get() and set() methods. Calling arr.set(x, y) from one thread will then guarantee that another thread callingarr.get(x) will read the value y (until another value is read to position x).

Solution 2: re-write the array reference after each field write

This is slightly kludgy and slightly inefficient (since what would be one write now involves two writes) but I believe it is theoretically correct. After setting an element of the array, we re-set the array reference to be itself:

volatile int[] arr = new int[...];...arr[4] = 100; arr = arr;

The marginal benefit of this technique could be:

  • it may allow you to fix some broken code with minimal changes if you have code already (incorrectly) using a volatile array and expecting thread-safe element access;
  • it saves us creating the wrapper object around the array (which is what happens with IntegerArray and LongArray);
  • it's more or less the only option for array types with no available atomic wrapper (e.g. a float array).

转载于:https://my.oschina.net/u/138995/blog/295300

你可能感兴趣的文章
Node.js this指针指向module.exports、global、实例,指针显式、隐式传递与绑定与优先级...
查看>>
H5以及Node读取excel
查看>>
Nginx基础篇(1)- Nginx的快速搭建和基本参数
查看>>
opencv python K-Means聚类
查看>>
从DevOps到AIOps,阿里如何实现智能化运维?
查看>>
Vue中的RouteMock实现思路及其问题
查看>>
前端每日实战:25# 视频演示如何用纯 CSS 创作一个慧星拖尾效果的 loader 动画...
查看>>
2018年第18周-Java语言思想-并发
查看>>
Node.js EventEmitter解读
查看>>
innodb事务隔离级别及实现机制
查看>>
分布式服务Dubbo的前世今生
查看>>
聊聊 WebRTC 网关服务器 2:如何选择 PeerConnection 方案?
查看>>
node项目部署杂记
查看>>
JavaScript由浅及深敲开原型链(二)
查看>>
关于CSS的个人理解
查看>>
java多线程(6)线程池
查看>>
WPF:Animation动画--CustomAnimation自定义动画(1)
查看>>
解决微信中页面回退ios不刷新的问题
查看>>
MongoDB基础操作
查看>>
JS设计模式入门和框架中的实践
查看>>