从另一个角度来看,Java内存模型是基于happens-before的。而你这里关于size的写入跟读取之间没有happens-before关系,那么很可能就读取不到最新的。而获取和释放一个锁之间具有happens-before关系,所以你把这个判断语句放到synchronized(Object.class)里面的话,应该也解决了这问题啦?
其实这里不加volatile应该也有另一种办法,题主能不能把文本代码发给我,我试试·····
=====================
感谢私信:).
首先我是从底层来看待这个问题(x86 cpu),你某个线程往一个变量里写入内容size++,那么只要你的写入操作本身是原子操作(cmpxchg,xadd,xchg),或者你的写入操作之后跟着一个原子操作(你这里的情况就是这样,size++后面有个释放锁的操作本身就是原子操作):
synchronized (Object.class) {
queue.add("a");
size++;
System.out.println("put " + queue.size());
}
那么这个size内存地址指代的内容,对于其他cpu就是可见的了。当然我们这里讨论的是线程,那么也就是说在操作系统的协调下,这个size内存地址指代的内容对于其他线程就是可见的了。但是为什么你的代码中还是没读到更新呢?我个人认为答案是Java内存模型的实现或者编译器的实现导致的。
所以站在解决这个问题的角度来看,我们并不需要把size生命为volatile变量,而只需要告诉编译器不要做多余的事,老老实实去读取那个size地址对应的变量,这里是我的方案:
import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import sun.misc.Unsafe;
public class Demo4 {
private static Queue queue = new LinkedList();
private static int size = 0;//声明成volatile不会死锁private static final int MAX = 10;
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
pool.execute(new Runnable() {
@Override
public void run() {
while (true) {
_unsafe.loadFence();
if (size < MAX) {
synchronized (Object.class) {
queue.add("a");
size++;
System.out.println("put " + queue.size());
}
}
}
}
});
pool.execute(new Runnable() {
@Override
public void run() {
while (true) {
_unsafe.loadFence();
if (size > 0) {
synchronized (Object.class) {
queue.poll();
size--;
System.out.println("get " + queue.size());
}
}
}
}
});
}
private static final Unsafe _unsafe = UtilUnsafe.getUnsafe();
private static class UtilUnsafe {
private UtilUnsafe() {
}
public static Unsafe getUnsafe() {
if (UtilUnsafe.class.getClassLoader() == null)
return Unsafe.getUnsafe();
try {
final Field fld = Unsafe.class.getDeclaredField("theUnsafe");
fld.setAccessible(true);
return (Unsafe) fld.get(UtilUnsafe.class);
} catch (Exception e) {
throw new RuntimeException("Could not obtain access to sun.misc.Unsafe", e);
}
}
}
}
这里的_unsafe.loadFence()会确保下面读取的size是更新过的。
或者你也可以值针对size变量,
import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import sun.misc.Unsafe;
public class Demo4 {
private static Queue queue = new LinkedList();
private static int size = 0;//声明成volatile不会死锁private static final Object size_base;
private static final long size_offset;
private static final int MAX = 10;
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
pool.execute(new Runnable() {
@Override
public void run() {
while (true) {
if (_unsafe.getIntVolatile(size_base, size_offset) < MAX) {
synchronized (Object.class) {
queue.add("a");
size++;
System.out.println("put " + queue.size());
}
}
}
}
});
pool.execute(new Runnable() {
@Override
public void run() {
while (true) {
if (_unsafe.getIntVolatile(size_base, size_offset) > 0) {
synchronized (Object.class) {
queue.poll();
size--;
System.out.println("get " + queue.size());
}
}
}
}
});
}
private static final Unsafe _unsafe = UtilUnsafe.getUnsafe();
static {
try {
size_base = _unsafe.staticFieldBase(Demo4.class.getDeclaredField("size"));
size_offset = _unsafe.staticFieldOffset(Demo4.class.getDeclaredField("size"));
} catch (Exception e) {
throw new Error(e);
}
}
private static class UtilUnsafe {
private UtilUnsafe() {
}
public static Unsafe getUnsafe() {
if (UtilUnsafe.class.getClassLoader() == null)
return Unsafe.getUnsafe();
try {
final Field fld = Unsafe.class.getDeclaredField("theUnsafe");
fld.setAccessible(true);
return (Unsafe) fld.get(UtilUnsafe.class);
} catch (Exception e) {
throw new RuntimeException("Could not obtain access to sun.misc.Unsafe", e);
}
}
}
}
换成变量地址(size_base + size_offset)的方式通过volatile直接读取,这样也可读到最新的size值。
我这个方案的意义是,size是在锁里面被写入 与 size生命为volatile变量, 这两者有点多余。:)