阻塞队列
来源:9-23 代码演示、源码分析

Mr_tang_
2020-01-21
1.阻塞队列定义是当插入和获取只要任意一个阻塞就是阻塞队列吗?
2.ArrayBlockingQueue消费者take后是怎样通知生产者队列里面少一个从而可以继续put呢?(我看dequeue的时候notFull.signal();原理不太懂)
1回答
-
问题1:阻塞队列的定义是实现BlockingQueue的类,从具体实现来看,是的,当插入和获取只要任意一个阻塞就是阻塞队列,因为有的容量无限的队列的插入操作是不会阻塞的。
问题2:take后,是通过Condition实现的通知其他线程,Condition的底层是利用LockSupport,而LockSupport是对UNSAFE的简单封装,所以重点是UNSAFE,在7-3的锚点中我们介绍过:
Unsafe是CAS的核心类。Java无法直接访问底层操作系统,而是通过本地(native)方法来访问。不过尽管如此,JVM还是开了一个后门,JDK中有一个类Unsafe,它提供了硬件级别的原子操作。
例如,AtomicInteger加载Unsafe工具,用来直接操作内存数据 :
public class AtomicInteger extends Number implements java.io.Serializable { // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } private volatile int value; public final int get() {return value;} }
在AtomicInteger数据定义的部分,我们还获取了unsafe实例,并且定义了valueOffset。再看到static块,懂类加载过程的都知道,static块的加载发生于类加载的时候,是最先初始化的,这时候我们调用unsafe的objectFieldOffset从Atomic类文件中获取value的偏移量,那么valueOffset其实就是记录value的偏移量的。
valueOffset表示的是变量值在内存中的偏移地址,因为Unsafe就是根据内存偏移地址获取数据的原值的,这样我们就能通过unsafe来实现CAS了。value是用volatile修饰的,保证了多线程之间看到的value值是同一份。
接下来继续看Unsafe的getAndAddInt方法的实现:
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
我们看var5获取的是什么,通过调用unsafe的getIntVolatile(var1, var2),这是个native方法,其实就是获取var1中,var2偏移量处的值。var1就是AtomicInteger,var2就是我们前面提到的valueOffset,这样我们就从内存里获取到现在valueOffset处的值了。
现在重点来了,compareAndSwapInt(var1, var2, var5, var5 + var4)其实换成compareAndSwapInt(obj, offset, expect, update)比较清楚,意思就是如果obj内的value和expect相等,就证明没有其他线程改变过这个变量,那么就更新它为update,如果这一步的CAS没有成功,那就采用自旋的方式继续进行CAS操作。
Unsafe的getAndAddInt方法分析:自旋 + CAS,在这个过程中,通过compareAndSwapInt比较并更新value值,如果更新失败,重新获取,然后再次更新,直到更新成功。
00
相似问题
回答 1
回答 2
回答 1
回答 1
回答 1