volatile、synchronized和原子操作
volatile
volatile是轻量级的synchronized,它在多处理器并发中保证了共享变量的“可见性”。可见性是指当一个线程修改一个共享变量时
,另外一个线程能读到这个修改的值(Java内存模型确保所有线程看到这个变量的值是一致的)。
volatile的两条实现原则
(1)Lock前缀指令会引起处理器缓存回写到内存。
(2)一个处理器的缓存回写到内存会导致其他处理器的缓存无效。
volatile的使用优化
使用追加到64字节的方式来填满高速缓冲区的缓存行,避免头节点和尾节点加载到同一个缓存行,使头、尾节点在修改时不会互相锁定。
synchronized实现原理
JVM基于进入和推出Monitor对象来实现方法同步和代码块同步,但两者实现细节不一样。
代码块同步使用monitorenter和monitorexit指令实现的。monitorenter指令在编译后插入到同步代码块的开始位置,而monitorexit是
插入到方法结束处和异常处,JVM要博爱正每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,
当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象monitor的所有权,即尝试获得对象的锁。
原子操作
java如何实现原子操作?
通过循环CAS的方式实现原子操作。JVM中CAS操作使用处理器提供的CMPXCHG指令实现。自旋CAS实现的基本思路就是循环及进行CAS操作指导成功为止。
使用CAS实现线程安全计数器
package concurrency;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
private AtomicInteger atomicI = new AtomicInteger(0);
private int i = 0;
public static void main(String[] args) {
final Counter cas = new Counter();
List<Thread> ts = new ArrayList<Thread>(600);
long start = System.currentTimeMillis();
for(int j = 0; j < 100; j++){
Thread t = new Thread(new Runnable(){
@Override
public void run(){
for(int i = 0; i < 10000; i++){
cas.count();
cas.safeCount();
}
}
});
ts.add(t);
}
for(Thread t : ts){
t.start();
}
//等待所有线程执行完成
for(Thread t : ts){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(cas.i);
System.out.println(cas.atomicI.get());
System.out.println(System.currentTimeMillis() - start);
}
//使用CAS实现线程安全计数器
private void safeCount(){
for(;;){
int i = atomicI.get();
boolean suc = atomicI.compareAndSet(i, ++i);
if(suc){
break;
}
}
}
//非线程安全计数器
private void count(){
i++;
}
}
CAS实现原子操作的三大问题
(1)ABA问题。可以使用版本号解决。JDK Atomic AtomicStampedReference可以解决ABA问题。
(2)循环时间长开销大。自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。
(3)只能保证一个共享变量的原子操作。对多个共享变量操作可以使用锁/将多个共享变量合并成一个共享变量来操作(两个共享变量i=2,j=a,合并以下ij=2a,然后采用CAS来操作)。