Java JUC之Atomic系列12大类实例讲解和原理分解(二)
}
int now = TEST_INTEGER.incrementAndGet();
System.out.println("我是线程:" + num + ",我得到值了,增加后的值为:" + now);
}
};
threads[i].start();
}
for(Thread t : threads) {
t.join();
}
System.out.println("最终运行结果:" + TEST_INTEGER.get());
}
}
代码例子中模拟多个线程并发对AtomicInteger进行增加1的操作,如果这个数据是普通类型,那么增加过程中出现的问题就是两个线程可能同时看到的数据都是同一个数据,增加完成后写回的时候,也是同一个数据,但是两个加法应当串行增加1,也就是加2的操作,甚至于更加特殊的情况是一个线程加到3后,写入,另一个线程写入了2,还越变越少,也就是不能得到正确的结果,在并发下,我们模拟计数器,要得到精确的计数器值,就需要使用它,我们希望得到的结果是11,可以拷贝代码进去运行后看到结果的确是11,顺然输出的顺序可能不一样,也同时可以证明线程的确是并发运行的(只是在输出的时候,征用System.out这个对象也不一定是谁先抢到),但是最终结果的确是11。
相信你对AtomicInteger的使用有一些了解了吧,要知道更多的方法使用,请参看这段代码中定义变量位置的注释,有关于AtomicInteger的相关方法的详细注释,可以直接跟踪进去看
源码,注释中使用了简单的描述说明了方法的用途。
而对于AtomicLong呢,其实和AtomicInteger差不多,唯一的区别就是它处理的数据是long类型的就是了;
对于AtomicBoolean呢,方法要少一些,常见的方法就两个:
[java]
AtomicBoolean#compareAndSet(boolean, boolean) 第一个参数为原始值,第二个参数为要修改的新值,若修改成功则返回true,否则返回false
AtomicBoolean#getAndSet(boolean) 尝试设置新的boolean值,直到成功为止,返回设置前的数据
因为boolean值就两个值,所以就是来回改,相对的很多增加减少的方法自然就没有了,对于使用来讲,我们列举一个boolean的并发修改,仅有一个线程可以修改成功的例子:
实例代码2:AtomicBooleanTest.java
[java]
import java.util.concurrent.atomic.AtomicBoolean;
public class AtomicBooleanTest {
/**
* 主要方法:
* @see AtomicBoolean#compareAndSet(boolean, boolean) 第一个参数为原始值,第二个参数为要修改的新值,若修改成功则返回true,否则返回false
* @see AtomicBoolean#getAndSet(boolean) 尝试设置新的boolean值,直到成功为止,返回设置前的数据
*/
public final static AtomicBoolean TEST_BOOLEAN = new AtomicBoolean();
public static void main(String []args) {
for(int i = 0 ; i < 10 ; i++) {
new Thread() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(TEST_BOOLEAN.compareAndSet(false, true)) {
System.out.println("我成功了!");
}
}
}.start();
}
}
}
这里有10个线程,我们让他们几乎同时去征用boolean值的修改,修改成功者输出:我成功了!此时你运行完你会发现只会输出一个“我成功了!”,说明征用过程中达到了锁的效果。
那么几种基本类型就说完了,我们来看看里面的实现是不是如我们开始说的Unsafe那样,看几段源码即可,我们看下AtomicInteger的一些源码,例如开始用的:incrementAndGet方法,这个,它的源码是:
[java]
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
可以看到内部有一个死循环,只有不断去做compareAndSet操作,直到成功为止,也就是修改的根本在compareAndSet方法里面,可以去看下相关的修改方法均是这样实现,那么看下compareAndSet方法的body部分是:
[java]
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
可以看到这里使用了unsafe的compareAndSwapInt的方法,很明显this就是指AtomicInteger当前的这个对象(这个对象不用像上面说的它不能是static和final,它无所谓的),而valueOffset的定义是这样的:
[java]
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) {
throw ne