学习《java并发编程的艺术》笔记:
Java代码在编译后是Java字节码,字节码被类加载到JVM里,JVM执行字节码,最终转换为汇编指令在CPU上执行。
Java中所使用的并发机制依赖于JVM的实现和CPU的指令。
1、volatile
2、synchronized
利用synchronized实现同步的基础:java中的每一个对象都可以作为锁。具体表现为三种形式:
-普通同步方法,锁是当前实例对象
-静态同步方法,锁是当前类的Class对象
-同步方法块,锁是Synchronized括号里配置的对象
Synchronized在JVM里的实现原理:
JVM基于进入和退出Monitor对象来实现方法同步和代码块同步——代码块同步是monitorenter和monitorexit指令实现的;方法同步可以使用这两个指令实现,也可以使用另一种方式实现。
monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入大哦方法结束处或异常处。任何对象都有一个monitor对象与之对应。
无锁–>偏向所–>轻量级所–>重量级锁(只能升级,为了提高获得锁和释放锁的效率)
3、原子操作的实现原理
CAS(Compare and Swap):比较并交换
原子操作(atomic operation):不可被中断的一个或一系列操作
处理器实现原子操作的方法:
1)通过总线锁保证原子性
2)通过缓存锁定保证原子性
有两种情况处理器不使用缓存锁定:
1)操作数据不能被缓存在处理器内部或操作的数据跨多个缓存行时,处理器使用总线锁定
2)处理器不支持缓存锁定
Java中实现原子操作的方式:
1)循环CAS实现原子操作:
JVM利用了处理器提供的CMPXCHG指令实现的。自旋CAS实现的基本思路就是循环进行CAS操作直到成功为止。
示例:基于CAS线程安全的计数器方法safeCount和一个非线程安全的计数器count
public class Counter {
private AtomicInteger atomicI=new AtomicInteger(0);//AtomicInteger用原子方式更新的int值
private int i=0;
/**
* 使用CAS实现线程安全计数器
*/
private void safeCount(){
for(;;){
int i=atomicI.get();
boolean suc=atomicI.compareAndSet(i,++i);
if(suc){
break;
}
}
}
/**
* 非线程安全计数器
*/
private void count(){
i++;
}
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<100;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);
}
}
运行结果:
9984
10000
14
JVM内部实现了很多锁机制:偏向所、轻量级锁和互斥锁,除了偏向锁,其他都用了循环CAS,即当一个线程想进入同步块的时候使用循环CAS的方式获取锁,退出同步块的时候使用循环CAS释放锁。
小节:JAVA中大部分容器和框架都依赖于volatile和原子操作的实现原理。深入了解对并发编程有帮助。