From:Java并发编程的艺术
- 目录
BiBi - 并发编程 -0- 开篇
BiBi - 并发编程 -1- 挑战
BiBi - 并发编程 -2- volatile
BiBi - 并发编程 -3- 锁
BiBi - 并发编程 -4- 原子操作
BiBi - 并发编程 -5- Java内存模型
BiBi - 并发编程 -6- final关键字
BiBi - 并发编程 -7- DCL
BiBi - 并发编程 -8- 线程
BiBi - 并发编程 -9- ReentrantLock
BiBi - 并发编程 -10- 队列同步器
BiBi - 并发编程 -11- 并发容器
BiBi - 并发编程 -12- Fork/Join框架
BiBi - 并发编程 -13- 并发工具类
BiBi - 并发编程 -14- 线程池
BiBi - 并发编程 -15- Executor框架
1. 实现原子操作的方法
第一个机制:通过【总线锁】保证原子性。处理器提供一个LOCK#信号,当一个处理器在总线上输出此信号时,其它处理器的请求将被阻塞住,那么该处理器可以独占共享内存。
总线锁把CPU和内存之间的通信锁住了,这使得锁定期间,其它处理器不能操作其它内存地址的数据,所以总线锁定的开销比较大,目前处理器在某些场合下使用第二个机制=>缓存锁,来替换总线锁来进行优化。【如volatile】
第二个机制:通过【缓存锁】保证原子性。在同一时刻,我们只需要保证对某个内存地址的操作是原子性即可。
内存区域如果被缓存在处理器的缓存行中,并且在Lock操作期间被锁定,那么当它执行锁操作回写到内存时,处理器不在总线上声言LOCK#信号,而是修改内部的内存地址,并允许它的缓存一致性机制来保证操作的原子性。
2. CAS
CAS【compare and swap】需要输入两个数值,一个旧值和一个新值,先比较旧值没有发生变化,才交换成新值。
通过循环CAS【自旋CAS】实现原子操作
public void safeCount(){
for( ; ; ){
int i = atomicI.get();
boolean b = atomicI.compareAndSet(i, ++i);
if (b){
break;
}
}
}
CAS实现原子操作的问题
1)ABA问题。一个值原来是A,变成了B,又变成了A,那么使用CAS时进行检查时会发现它的值没有发生变化。解决思路:使用版本号,在变量前面追加上版本号,每次变量更新使版本号加1,那么A>B>A就变成了1A>2B<3A。Java1.5提供了类AtomicStampedReference来解决ABA问题。
2)循环时间长开销大。自旋CAS如果长时间不成功,给CPU带来很大开销。
3)只能保证一个共享变量的原子操作。可以通过把多个共享变量合并成一个共享变量来改进。Java1.5提供AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里进行CAS操作。
提示:除了偏向锁,JVM其它锁的实现方式都用了循环CAS,即使用CAS的方式来获取/释放锁。
3. 原子类 - Atomic
原子更新基础类型
AtomicBoolean、AtomicInteger、AtomicLong。
分析AtomicInteger源码:
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe(); //包装类
private static final long VALUE;
static {
try {
VALUE = U.objectFieldOffset
(java.util.concurrent.atomic.AtomicInteger.class.getDeclaredField("value"));
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
private volatile int value; //volatile类型
public AtomicInteger(int initialValue) {
value = initialValue;
}
public final int getAndIncrement() {
return U.getAndAddInt(this, VALUE, 1);
}
public final boolean compareAndSet(int expect, int update) {
return U.compareAndSwapInt(this, VALUE, expect, update);
}
public final boolean compareAndSet(int expect, int update) {
return U.compareAndSwapInt(this, VALUE, expect, update);
}
}
Atomic包里的类都是使用Unsafe实现的包装类。Unsafe中提供了3种CAS方法:compareAndSwapObject、compareAndSwapInt、compareAndSwapLong。
原子更新数组
AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray。
AtomicIntegerArray使用例子:
package com.ljg;
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicIntegerArrayTest {
static int[] value = new int[] { 1, 2 };
static AtomicIntegerArray ai = new AtomicIntegerArray(value);
public static void main(String[] args) {
ai.getAndSet(0, 3);
System.out.println(ai.get(0)); //3
System.out.println(value[0]); //1,保持不变
}
}
可见AtomicIntegerArray对内部的数组元素修改时,不会影响传入的数组。
原子更新引用类型
因为AtomicInteger只能更新一个变量,如果要原子更新多个变量,就需要使用原子更新对象引用。
AtomicReference、AtomicReferenceFieldUpdater、AtomicMarkableReference。
AtomicReference使用例子:
package com.ljg;
import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceTest {
public static AtomicReference<User> atomicRef = new AtomicReference<User>();
public static void main(String[] args) {
User user = new User("lili", 15);
atomicRef.set(user);
User updateUser = new User("liLei", 18);
atomicRef.compareAndSet(user, updateUser);
System.out.println(atomicRef.get().getName());
System.out.println(atomicRef.get().getAge());
}
}
原子更新字段类
AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedReference。
想要原子地更新某个类里的某个字段,需要该字段属性必须使用public volatile修饰符。
AtomicIntegerFieldUpdater使用例子:
package com.ljg;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
public class AtomicIntegerFieldUpdaterTest {
//创建更新器
private static AtomicIntegerFieldUpdater a =
AtomicIntegerFieldUpdater.newUpdater(User.class, "age");
public static void main(String[] args) {
User conan = new User("conan", 10);
System.out.println(a.getAndIncrement(conan)); //10
System.out.println(a.get(conan)); //11
}
}
网友评论