一个类暴露出的借口只有一个Object(int[] 可以看做是一个Object),我当时是用写加锁,读不加锁的方式。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的赋值,可能不是原子操作,所以改为了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,我不知道怎样写,谁能给个示例吗?谢谢!
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的赋值,可能不是原子操作,所以改为了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,我不知道怎样写,谁能给个示例吗?谢谢!
2.long与double的赋值,引用是可以分割的,非原子操作;
3.要在线程间共享long或double的字段时,必须在synchronized中操作,或是声明成volatile.所以,你这种赋值,不需要锁。
——看看Thinking In Java吧,这书确实不错。否是说values = new_values;实际已经改变了values所指向的内存地址,而不是将new_values里面的东西赋值给values了?
——这是必然的,你所期望的赋值,只有原始类型才有,对象全都是引用。我看AtomicLong里面的实现,用了volatile long。
——这是因为long和double,超出32位,也就是高位和低位的赋值会分为两条语句执行;所任默认情况下就无法实现原子性了可是volatile能够保证原子性吗?我只知道volatile可以避免cpu cache引起的错误,能够保证每次都操作内存。
——两种能力都提供了,或者说因为volatile要提供可见性,所以也就必须保证其原子性
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.再次感谢ldh911 !!
(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]引用類型可能是由於引用只需一步操作,所以原子
public class Visibility {
private static boolean stop;
public static void main(String[] args) throws Exception {
new Thread(new Runnable(){
public void run() {
int i = 0;
while(!stop) {
i++;
}
System.out.println("finish loop,i=" + i);
}
}).start();
Thread.sleep(1000);
stop = true;
Thread.sleep(2000);
System.out.println("finish main");
}
}
在hotspot虚拟机上这样执行: java -server Visibility 会死循环,循环的那个线程永远也看不到stop值被修改过了
可能是jls強制jvm實現的內部原子化操作
够狠,不带任何io和yield()之类的高密度计算,是个很不错的例子。
cache在哪里?线程对象么?new ThreadPool的时候带了ThreadFactory参数?如果是这样的话,就有看线程池的策略了Executors类大部分new出来的线程池都是ThreadPoolExecutor类的对象(有个别不是),ThreadPoolExecutor类的构造方法传了很多参数,灵活控制了线程池的行为。至于线程会不会被回收,就要看传入的参数了,具体含义可以ThreadPoolExecutor的javadoc中找到
public Visibility();
Code:
0: aload_0
1: invokespecial #2; //Method java/lang/Object."<init>":()V
4: returnpublic static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: new #3; //class java/lang/Thread
3: dup
4: new #4; //class Visibility$1
7: dup
8: invokespecial #5; //Method Visibility$1."<init>":()V
11: invokespecial #6; //Method java/lang/Thread."<init>":(Ljava/lang/Runna
ble;)V
14: invokevirtual #7; //Method java/lang/Thread.start:()V
17: ldc2_w #8; //long 1000l
20: invokestatic #10; //Method java/lang/Thread.sleep:(J)V
23: iconst_1
24: putstatic #1; //Field stop:Z
27: ldc2_w #11; //long 2000l
30: invokestatic #10; //Method java/lang/Thread.sleep:(J)V
33: getstatic #13; //Field java/lang/System.out:Ljava/io/PrintStream;
36: ldc #14; //String finish main
38: invokevirtual #15; //Method java/io/PrintStream.println:(Ljava/lang/St
ring;)V
41: returnstatic boolean access$000();
Code:
0: getstatic #1; //Field stop:Z
3: ireturn}public class Visibility {
private static boolean stop;
public static void main(String[] args) throws Exception {
new Thread(new Runnable(){
public void run() {
int i = 0;
while(!stop) {
i++;
System.out.println("stop=" + stop);
}
System.out.println("finish loop,i=" + i);
}
}).start();
Thread.sleep(1000);
stop = true;
Thread.sleep(2000);
System.out.println("finish main");
}
}
其实那个例子不加volatile在java -client Visibility的时候也不会有问题
问题在于-server的时候虚拟机会去优化,而他什么条件下会优化,怎么去优化,我们根本无法预测
private static boolean stop;
public static void main(String[] args) throws Exception {
while(!stop) {
System.out.println("stop=" + stop);
}
}
}public class Test extends java.lang.Object{
public Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: returnpublic static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: getstatic #2; //Field stop:Z
3: ifne 36
6: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
9: new #4; //class java/lang/StringBuilder
12: dup
13: invokespecial #5; //Method java/lang/StringBuilder."<init>":()V
16: ldc #6; //String stop=
18: invokevirtual #7; //Method java/lang/StringBuilder.append:(Ljava/lang/
String;)Ljava/lang/StringBuilder;
21: getstatic #2; //Field stop:Z
24: invokevirtual #8; //Method java/lang/StringBuilder.append:(Z)Ljava/lan
g/StringBuilder;
27: invokevirtual #9; //Method java/lang/StringBuilder.toString:()Ljava/la
ng/String;
30: invokevirtual #10; //Method java/io/PrintStream.println:(Ljava/lang/St
ring;)V
33: goto 0
36: return}表現差異頗大
嗯,我就是怕Cache在线程对象中。因为线程池似乎可以有任务队列,一个active的线程可能执行完一个任务,就直接从队列里领取一个任务继续执行了,这样的话,可能并不会清缓存。
嗯,我觉得你说的“线程的上下文线程自己带着的,和线程池没关系”很有道理。线程的上下文,确实不是线程池能干预的,而是取决于jvm的行为(或者它所依赖的OS?),它认为该从内存中读,就会生成一个读内存的汇编指令,否则可能就生成一个读堆栈的汇编指令。
volatile long count = 0; public static void main(String[] args) throws InterruptedException {
final TestVolatile tv = new TestVolatile();
for (int i = 0; i < 200; i++) {
new Thread() {
@Override
public void run() {
tv.updateCount();
} }.start();
}
Thread.sleep(10000);
System.out.println(tv.count);
} public void updateCount() {
for (int i = 0; i < 500; i++) {
count = count + 1;
}
} public long getCount() {
return count;
}
}
volatile保证不了原子性,count<200*500.
你说的这个“原子性”和我们说的不是一个概念。32楼所说的也正是我关心的,就是在读或写(不是更新)的操作是否是原子的,注意,这仅仅是一个操作,java规范jsr133说明,volatile可以保证long和double的读或写是原子的。而你说的这个是read-update-write的操作,这必然是需要加锁的。
1.5之前,volatile無法保證64位的long型變量的讀或存(load or store)
當第二個線程恰巧在只有32位的新值被存入進入時去讀的話,得到的就是一半舊值一半新值,即32位是舊的,另32位是新的,這就是讀寫不原子了
首先,我认为read和load没什么区别,store和write也是。例如a = b,转换成汇编可能就是mov b, register
mov register, a既然a可以被任意赋值,自然就不能是final的。我怕的是,当数据为long时,汇编代码是这样的mov high32 of b, register x
mov low32 of b, register y
mov register x, high32 of a
mov register y, low32 of a当在第三句执行完,第四句没有执行时,另一个线程读取了a,这时a就不是一个完整的了。volatile可以避免这一点。