java多线程更新时的问题,请大牛解释。
一个类暴露出的借口只有一个Object(int[] 可以看做是一个Object),我当时是用写加锁,读不加锁的方式。
- Java code
class A { private int[] values = new int[10]; private ReentrantLock lock = new ReentrantLock(); public int[] get_all() { return values; } public void update() { lock.lock(); try { new_values = new int[11]; values = new_values; } finally { lock.unlock(); } }}后来觉得在values = new_values这一步,涉及到一个Object的赋值,可能不是原子操作,所以改为了
- Java code
class A { private int[] values = new int[10]; private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public int[] get_all() { lock.readLock().lock(); try { return values; } finally { lock.readLock().unlock(); } } public void update() { lock.writeLock().lock(); try { new_values = new int[11]; values = new_values; } finally { lock.writeLock().unlock(); } }}请问我这样考虑是否正确?有什么效率更高的方法吗?
在C++中,我可以将结果封装在一个指针里,直接传递回一个指针,而指针的赋值是可以保证原子性的。
我对java不太熟悉,有人说Object的native实现里,可能包含了多个变量,所以赋值不可能是原子的。
也有人说,可以用AtomicReference,我不知道怎样写,谁能给个示例吗?谢谢!
[解决办法]
1.基本类型,引用类型的赋值,引用是原子的操作;
2.long与double的赋值,引用是可以分割的,非原子操作;
3.要在线程间共享long或double的字段时,必须在synchronized中操作,或是声明成volatile.
所以,你这种赋值,不需要锁。
[解决办法]
在方法声明中加synchronized
[解决办法]
[解决办法]
请问哪里有比较官方的介绍吗?
——看看Thinking In Java吧,这书确实不错。
否是说values = new_values;实际已经改变了values所指向的内存地址,而不是将new_values里面的东西赋值给values了?
——这是必然的,你所期望的赋值,只有原始类型才有,对象全都是引用。
我看AtomicLong里面的实现,用了volatile long。
——这是因为long和double,超出32位,也就是高位和低位的赋值会分为两条语句执行;所任默认情况下就无法实现原子性了
可是volatile能够保证原子性吗?我只知道volatile可以避免cpu cache引起的错误,能够保证每次都操作内存。
——两种能力都提供了,或者说因为volatile要提供可见性,所以也就必须保证其原子性
[解决办法]
读的时候也是要加锁的
不管赋值是不是原子,values是非volatile的,当a线程给其赋值后,b线程未必可见,也就说b线程看到的可能还是老的值
[解决办法]
并发度和取舍问题。如果并发频密,读取到老数据的概率就会很高。
其实volatile的开销相比synchronized,低很多的。
[解决办法]
如果只是你提供的代码,只需要把values 定义成volatile即可,get和update都不需要同步锁。不管是32位还是64位jvm,都必需保证volatile变量的取值和赋值是原子操作。
[解决办法]
http://en.wikipedia.org/wiki/Volatile_variable
The Java programming language also has the volatile keyword, but it is used for a somewhat different purpose. When applied to a field, the Java volatile guarantees that:
(In all versions of Java) There is a global ordering on the reads and writes to a volatile variable. This implies that every thread accessing a volatile field will read its current value before continuing, instead of (potentially) using a cached value. (However, there is no guarantee about the relative ordering of volatile reads and writes with regular reads and writes, meaning that it's generally not a useful threading construct.)
(In Java 5 or later) Volatile reads and writes establish a happens-before relationship, much like acquiring and releasing a mutex.[8]
Using volatile may be faster than a lock, but it will not work in some situations.[citation needed] The range of situations in which volatile is effective was expanded in Java 5; in particular, double-checked locking now works correctly.[9]
引用型可能是由於引用只需一步操作,所以原子
[解决办法]
[解决办法]
JLS 17.7 Non-atomic Treatment of double and long
Some implementations may find it convenient to divide a single write action on a 64-bit long or double value into two write actions on adjacent 32 bit values. For efficiency's sake, this behavior is implementation specific; Java virtual machines are free to perform writes to long and double values atomically or in two parts.
For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64 bit value from one write, and the second 32 bits from another write. Writes and reads of volatile long and double values are always atomic. Writes to and reads of references are always atomic, regardless of whether they are implemented as 32 or 64 bit values.
VM implementors are encouraged to avoid splitting their 64-bit values where possible. Programmers are encouraged to declare shared 64-bit values as volatile or synchronize their programs correctly to avoid possible complications.
jls中是的,但如果在32位jvm上64位引用,是如何保原子的呢
可能是jls制jvm的部原子化操作
[解决办法]
[解决办法]
[解决办法]
[解决办法]
[解决办法]
[解决办法]
再一下,看一下的while循
- Java code
public class Test { private static boolean stop; public static void main(String[] args) throws Exception { while(!stop) { System.out.println("stop=" + stop); } }}
[解决办法]
[解决办法]
[解决办法]