Java分布式应用学习笔记04JDK的并发包的集合总结---后篇
唉~这一大篇blog又是只能显示部分,部分内容被截断了。。。。
4. Set的并发
CopyOnWriteArraySet和CopyOnWriteArrayList底层实现差不多,就是在添加元素的时候需要对对象进行唯一性判断,如果对象数组已经含有重复的元素,不进行增加处理。在此不再赘述。
5. Queue的并发
队列的并发类是java.util.concurrent.ArrayBlockingQueue,从类名字上大家估计就能猜出来了,底层使用的依然是数组。这个ArrayBlockingQueue是继承自原始的java.util.AbstractQueue,所以很多方法在父类里面已经有了,只是对于关键方法入队列、出队列操作加入了锁对儿机制。
入队列元素操作源码如下
Java代码
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (e == null) throw new NullPointerException();
g nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
if (count != items.length) {
insert(e);
return true;
}
if (nanos <= 0)
return false;
try {
nanos = notFull.awaitNanos(nanos);
} catch (InterruptedException ie) {
notFull.signal(); // propagate to non-interrupted thread
throw ie;
}
}
} finally {
lock.unlock();
}
}
数组未满情况下,执行insert操作的时候,如果数组满了,则进行等待,单位是纳秒。如果超时或者被唤醒了,那么再次判断是否数组已满,如果线程被打断直接抛出异常。出队列方法和入队列差不多,不再赘述。
6. AtomicXXXX的原子类
并发包还提供了支持原子操作的Atomic系列类,我们举一个具有代表性的类——AtomicInteger类,通常我们使用计数器操作的时候,一般为了避免线程安全的问题,在方法上加锁操作。有了并发包下的原子系列类,我们直接使用即可。关键使用代码片段如下
Java代码
public static int getSum() {
return sum.incrementAndGet();
}
其自增方法底层片段最关键是这么一句
Java代码
if (compareAndSet(current, next))
return next;
此方法具体如下
Java代码
public final boolean compareAndSet(int expect, int update) {
urn unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
unsafe.compareAndSwapInt调用了本地native方法直接与底层硬件也就是CPU打交道。大家有兴趣的话可以将sun的这段代码反编译看看。底层会比较内存上的地址上的值是否为当前值,是就next,不是则反复循环,直到找到当前值。这个和Hibernate的那个乐观锁有异曲同工的意思。其他的一些原子类AtomicBoolean、AtomicLong等等在此不再赘述,使用以及底层原理都差不多。
7. 总结与反思
关于并发包的集合类就先总结到这里,这次没有将集合的读取元素进行性能对比,实际应用中高并发的读取比集合元素改变(add、remove、replace)更为常见,不过代码很简单,所以就不给出了,有兴趣的朋友认识了这些类后可以自己做实验。至于反思,应该就是这些并发包的资源性能,是否很占用内存空间,加入在一个高并发环境下而且硬件环境又不允许分配给应用系统十分宽容的硬件资源,那么高并易做图况下是否玩不转(比如云计算的虚拟化,一台实机可能启动多个虚拟机,作为实机的扩充)。这个问题我们可以使用工具测试jconsole进行监控,也有可能用户的应用自身代码也存在着一系列的问题,还是得具体问题具体分析,总的来说并发包要想实现线程安全,而且时间效率在一般环境下又和非并发包的差不多,需要消耗的内存资源比以前多是一定的,这个是避免不了的,世界是物质的,做任何事情都需要付出代价,只是看这个代价和收益相比值不值得。
补充:软件开发 , Java ,