Java并发机制的底层实现原理
2017-11-11 20:07
696 查看
Volatile
它是轻量级的synchronized,比之执行成本更低,因为它不会引起线程的上下文切换,它在多处理器开发中保证了共享变量的“可见性”,“可见性”的意思是当一个线程修改一个变量时,另外一个线程能读到这个修改的值。
被volatile修饰的变量,在被执行赋值操作时,它使用lock前缀的汇编指令,保证
1,将当前处理器缓存行的数据写回到系统内存,
2,这个写回内存的操作会使其在其他CPU里缓存了该内存地址数据无效,这样其它cpu再想获取到该值时,会重新从内存中读取数据
上面两条规则,就是著名的“缓存一致性原则”
Synchronized
java中每一个对象都可以成为锁,具体表现为:
1,对于普通同步方法,锁是当前实例对象
2,对于静态同步方法,锁是当前类的class对象
3,对于同步方法块,锁是synchronized括号里配置的对象
同步代码块是使用monitorenter和monitorexit指令实现的,每个对象都有一个monitor与之相关联,当且仅当它被持有后,它将处于锁定状态
synchronized的锁存储在java对象头里面,锁一共有4中状态,依次是:无锁状态<偏向锁状态<轻量级锁状态<重量级锁状态, 另外随着竞争情况,锁会升级,而且升级后不能降级,这样做是为了保证获得锁和释放锁的效率.
偏向锁: 现实中锁不仅不存在多线程竞争,而且总是被同一个线程获得,为了降低线程获得锁的开销,引入偏向锁。偏向锁的撤销只有当竞争出现的时候才会发生。
轻量级锁:通过CAS自旋的方式,没有获取资源的线程不必阻塞,也就不会有上下文的切换,从而降低了开销,但是CAS自旋也是消耗CPU的,所以轻量级锁使用于同步块执行速度快,锁的持有时间比较短的情况,响应速度比较快
重量级锁:线程之间竞争不存在自旋的情况,直接阻塞,上下文切换,系统开销比较大,适用于同步块所需要的时间比较久的情况,系统吞吐量比较大
原子操作的实现原理
理解几个术语
1,缓存行,cache line,缓存的最小操作单位
2,比较并交换,compare and exchange, CAS操作需要输入两个值,一个old value(也是expected value),一个new value,在操作期间,如果旧值发生了变化,则新值不会交换,这个就是为了解决例如 i++这种情况的,i读取,i++, i被赋值,但是如果多个线程同时访问这个i值,则下一个线程读取到的可能是脏数据,这时候新值就不该被赋给变量,自旋CAS的实现原理就是,当这种情况发生时,放弃赋值,重新尝试,直到成功为止
3,CPU流水线,一条指令被分解成多个步骤,每个步骤在不同的电器元件上执行,类似于车间流水线,这样可以提高效率
4,内存顺序冲突,这个概念我稍微有点疑惑,它是由假共享引起的,是指多个CPU同时修改一个缓存行的不同部分引起其中一个CPU的操作无效,当出现这种情况时,CPU流水线会被清空,这个清空我自己的理解是说,后续指令需要依赖前面指令才能成功,如果前面缓存更新失效了,后续指令需要重新执行
处理器如何实现原子操作呢
1,总线加锁实现原子操作
2,缓存加锁实现原子操作
Java如何实现原子操作呢
1,使用循环CAS实现原子操作
2,使用锁实现原子操作
下面附上一段循环CAS代码
输出结果:
Unsafe count: 99498
Safe count: 100000
Java中有很多原子操作类,如AtomicInteger,AtomicBoolean,它们都是使用自旋CAS实现原子操作的。可以查看AtomicInteger.getAndIncrement()源码
它是轻量级的synchronized,比之执行成本更低,因为它不会引起线程的上下文切换,它在多处理器开发中保证了共享变量的“可见性”,“可见性”的意思是当一个线程修改一个变量时,另外一个线程能读到这个修改的值。
被volatile修饰的变量,在被执行赋值操作时,它使用lock前缀的汇编指令,保证
1,将当前处理器缓存行的数据写回到系统内存,
2,这个写回内存的操作会使其在其他CPU里缓存了该内存地址数据无效,这样其它cpu再想获取到该值时,会重新从内存中读取数据
上面两条规则,就是著名的“缓存一致性原则”
Synchronized
java中每一个对象都可以成为锁,具体表现为:
1,对于普通同步方法,锁是当前实例对象
2,对于静态同步方法,锁是当前类的class对象
3,对于同步方法块,锁是synchronized括号里配置的对象
同步代码块是使用monitorenter和monitorexit指令实现的,每个对象都有一个monitor与之相关联,当且仅当它被持有后,它将处于锁定状态
synchronized的锁存储在java对象头里面,锁一共有4中状态,依次是:无锁状态<偏向锁状态<轻量级锁状态<重量级锁状态, 另外随着竞争情况,锁会升级,而且升级后不能降级,这样做是为了保证获得锁和释放锁的效率.
偏向锁: 现实中锁不仅不存在多线程竞争,而且总是被同一个线程获得,为了降低线程获得锁的开销,引入偏向锁。偏向锁的撤销只有当竞争出现的时候才会发生。
轻量级锁:通过CAS自旋的方式,没有获取资源的线程不必阻塞,也就不会有上下文的切换,从而降低了开销,但是CAS自旋也是消耗CPU的,所以轻量级锁使用于同步块执行速度快,锁的持有时间比较短的情况,响应速度比较快
重量级锁:线程之间竞争不存在自旋的情况,直接阻塞,上下文切换,系统开销比较大,适用于同步块所需要的时间比较久的情况,系统吞吐量比较大
原子操作的实现原理
理解几个术语
1,缓存行,cache line,缓存的最小操作单位
2,比较并交换,compare and exchange, CAS操作需要输入两个值,一个old value(也是expected value),一个new value,在操作期间,如果旧值发生了变化,则新值不会交换,这个就是为了解决例如 i++这种情况的,i读取,i++, i被赋值,但是如果多个线程同时访问这个i值,则下一个线程读取到的可能是脏数据,这时候新值就不该被赋给变量,自旋CAS的实现原理就是,当这种情况发生时,放弃赋值,重新尝试,直到成功为止
3,CPU流水线,一条指令被分解成多个步骤,每个步骤在不同的电器元件上执行,类似于车间流水线,这样可以提高效率
4,内存顺序冲突,这个概念我稍微有点疑惑,它是由假共享引起的,是指多个CPU同时修改一个缓存行的不同部分引起其中一个CPU的操作无效,当出现这种情况时,CPU流水线会被清空,这个清空我自己的理解是说,后续指令需要依赖前面指令才能成功,如果前面缓存更新失效了,后续指令需要重新执行
处理器如何实现原子操作呢
1,总线加锁实现原子操作
2,缓存加锁实现原子操作
Java如何实现原子操作呢
1,使用循环CAS实现原子操作
2,使用锁实现原子操作
下面附上一段循环CAS代码
import java.sql.Array; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class AtomicJava { private static AtomicInteger atomicI = new AtomicInteger(); private static int i = 0; public void count(){ /* 自旋CAS(compareAndSet)的基本思路就是循环进行CAS操作直到成功为止 * atomicI.getAndIncrement(); public final int getAndIncrement() { for (;;) {// int current = get(); int next = current + 1;// current和next不存在被竞争的情况,但是get()获取到执行compareAndSet之间,很可能原值已经被其它线程改变了 if (compareAndSet(current, next)) return current; } }*/ } public static void main(String[] args){ final AtomicJava counter=new AtomicJava(); List<Thread> list=new ArrayList<Thread>(); for(int i=0;i<100;i++){ Thread t=new Thread(new Runnable(){ @Override public void run() { for(int j=0;j<1000;j++){ counter.safeCount(); counter.unSafeCount(); } } }); list.add(t); } for(Thread t:list){ t.start(); } try { Thread.sleep(1000); for (Thread t : list) { t.join(); } System.out.println("Unsafe count: "+i); System.out.println("Safe count: "+atomicI.get()); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 使用CAS实现线程安全计数器 */ private void safeCount() { for (;;) { int i = atomicI.get(); boolean suc = atomicI.compareAndSet(i, ++i); if(suc) return; } } /** * 非线程安全的count */ private void unSafeCount(){ i++; } }
输出结果:
Unsafe count: 99498
Safe count: 100000
Java中有很多原子操作类,如AtomicInteger,AtomicBoolean,它们都是使用自旋CAS实现原子操作的。可以查看AtomicInteger.getAndIncrement()源码
相关文章推荐
- Java并发机制的底层实现原理(二)
- java并发机制的底层实现原理
- 2、Java并发机制的底层实现原理
- [jjzhu学java之多线程笔记]java并发机制的底层实现原理
- Java并发机制底层——Volatile的实现原理
- 深入学习Java多线程——并发机制底层实现原理
- Java并发机制和底层实现原理
- Java并发-Java并发机制的底层实现原理
- java并发机制的底层实现原理
- 第二章-Java并发机制的底层实现原理
- java并发编程的艺术【二】java并发机制的底层实现原理
- 二、Java并发机制的底层实现原理
- 并发编程笔记(二):Java 并发机制的底层实现原理
- Java并发编程(二): Java并发机制的底层实现原理
- Java并发编程的艺术--第二章<Java并发机制的底层实现原理>
- java并发机制的底层实现原理
- Java并发机制底层——原子操作的实现原理
- JAVA并发机制的底层实现原理
- 程序猿大牛:分享JAVA并发机制的底层实现原理详解(附电子书籍)
- java并发机制的底层实现原理