Java JUC之Atomic系列12大类实例讲解和原理分解(五)
*
* @see AtomicIntegerArray#getAndAdd(int, int) 和addAndGet类似,区别是返回值是变化前的数据
* @see AtomicIntegerArray#getAndDecrement(int) 和decrementAndGet类似,区别是返回变化前的数据
* @see AtomicIntegerArray#getAndIncrement(int) 和incrementAndGet类似,区别是返回变化前的数据
* @see AtomicIntegerArray#getAndSet(int, int) 将对应下标的数字设置为指定值,第二个参数为设置的值,返回是变化前的数据
*/
private final static AtomicIntegerArray ATOMIC_INTEGER_ARRAY = new AtomicIntegerArray(new int[]{1,2,3,4,5,6,7,8,9,10});
public static void main(String []args) throws InterruptedException {
Thread []threads = new Thread[100];
for(int i = 0 ; i < 100 ; i++) {
final int index = i % 10;
final int threadNum = i;
threads[i] = new Thread() {
public void run() {
int result = ATOMIC_INTEGER_ARRAY.addAndGet(index, index + 1);
System.out.println("线程编号为:" + threadNum + " , 对应的原始值为:" + (index + 1) + ",增加后的结果为:" + result);
}
};
threads[i].start();
}
for(Thread thread : threads) {
thread.join();
}
System.out.println("=========================>\n执行已经完成,结果列表:");
for(int i = 0 ; i < ATOMIC_INTEGER_ARRAY.length() ; i++) {
System.out.println(ATOMIC_INTEGER_ARRAY.get(i));
}
}
}
计算结果说明:100个线程并发,每10个线程会被并发修改数组中的一个元素,也就是数组中的每个元素会被10个线程并发修改访问,每次增加原始值的大小,此时运算完的结果看最后输出的敲好为原始值的11倍数,和我们预期的一致,如果不是线程安全那么这个值什么都有可能。
而相应的类:AtomicLongArray其实和AtomicIntegerArray操作方法类似,最大区别就是它操作的数据类型是long;而AtomicRerenceArray也是这样,只是它方法只有两个:
[java]
AtomicReferenceArray#compareAndSet(int, Object, Object)
参数1:数组下标;
参数2:修改原始值对比;
参数3:修改目标值
修改成功返回true,否则返回false
AtomicReferenceArray#getAndSet(int, Object)
参数1:数组下标
参数2:修改的目标
修改成功为止,返回修改前的数据
到这里你是否对数组内部的操作应该有所了解了,和当初预期一样,参数就是多了一个下标,为了完全验证这点,跟踪到源码中可以看到:
[java]
public final int addAndGet(int i, int delta) {
while (true) {
int current = get(i);
int next = current + delta;
if (compareAndSet(i, current, next))
return next;
}
}
可以看到根据get(i)获取到对应的数据,然后做和普通AtomicInteger差不多的操作,get操作里面有个细节是:
[java]
public final int get(int i) {
return unsafe.getIntVolatile(array, rawIndex(i));
}
这里通过了unsafe获取基于volatile方式获取(可见性)获取一个int类型的数据,而获取的位置是由rawIndex来确定,它的源码是:
[java]
private long rawIndex(int i) {
if (i < 0 || i >= array.length)
throw new IndexOutOfBoundsException("index " + i);
return base + (long) i * scale;
}
可以发现这个结果是一个地址位置,为base加上一耳光偏移量,那么看看base和scale的定义为:
[java]
private static final int base = unsafe.arrayBaseOffset(int[].class);
private static final int scale = unsafe.arrayIndexScale(int[].class);
可以发现unsafe里面提供了对数组base的位置的获取,因为对象是有头部的,而数组还有一个长度位置,第二个很明显是一个数组元素所占用的宽度,也就是基本精度;这里应该可以体会到unsafe所带来的强大了吧。
本文最后要介绍的部分为Updater也就是修改器,它算是Atomic的系列的一个扩展,Atomic系列是为你定义好的一些对象,你可以使用,但是如果是别人已经在使用的对象会原先的代码需要修改为Atomic系列,此时若全部修改类型到对应的对象相信很麻烦,因为牵涉的代码会很多,此时java提供一个外部的Updater可以对对象的属性本身的修改提供类似Atomic的操作,也就是它对这些普通的属性的操作是并发下安全的,分别由:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceUpdater,这样操作后,
系统会更加灵活,也就是可能那些类的属性只是在某些情况下需要控制并发,很多时候不需要,但是他们的使用通常有以下几个限制:
限制1:操作的目标不能是stati