看完后,你再也不用怕面试问并发编程啦( 七 )


代码:package cn.itcast.thread;public class Test7 { // 无法在多线程的情况下实现原子自递增的问题 。private static int count = 0; // 定义累计的方法 public synchronized static void inc(){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } count++; } public static void main(String[] args) throws InterruptedException { // 循环创建线程 for(int i = 0; i < 1000; i++){ new Thread(() -> { inc(); }).start(); } Thread.sleep(4000); System.out.println("y运行结果:"+count); }} 9.3 有序性Java内存模型中,允许编辑器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性 。
从字面上的意思理解,有序就是要保证代码按照既定的顺序依次执行 。但是CPU(或编译器)出于性能优化的目的,在保证不会对程序运行结果产生影响的前提下,代码的执行顺序可能会和我们既定的顺序不一致 。
示例
int i = 1;
int j = 1;
这两行代码互相没有任何依赖关系,谁先执行还是后执行,对程序运行结果都不会有什么影响 。经过指令重排后,可能 int j = 1; 就比int i = 1;先执行了 。不同的CPU架构可能支持不同的重排规则,像Load-Load、Load-Store、Store-Store、Store-Load等等 。
指令重排的后果在并发的情况下有时会是严重的,比如以下代码:public void execute(){ int a = 0; int b = 1; int c = a + b;} 这里a=0,b=1两句可以随便排序,不影响程序逻辑结果,但c=a+b这句必须在前两句的后面执行 。
从前面那个例子可以看到,重排序在多线程环境下出现的概率还是挺高的 , 在关键字上有volatile和synchronized可以禁用重排序 。
可能上面说的比较绕,举个简单的例子:// x、y为非volatile变量// flag为volatile变量 x = 2; //语句1y = 0; //语句2flag = true; //语句3x = 4; //语句4y = -1; //语句5 10、Java中的锁:锁的种类在面试过程时,经常会被问到各种各样的锁,如乐观锁、读写锁等等,非常繁多,在此做一个总结 。介绍的内容如下:
• 乐观锁/悲观锁
• 独享锁/共享锁
• 互斥锁/读写锁
• 可重入锁
• 公平锁/非公平锁
• 分段锁
• 偏向锁/轻量级锁/重量级锁
• 自旋锁
乐观锁/悲观锁
乐观锁与悲观锁并不是特指某两种类型的锁,是人们定义出来的概念或思想,主要是指看待并发同步的角度 。
乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制 。乐观锁适用于多读的应用类型,这样可以提高吞吐量,在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS(Compare and Swap 比较并交换)实现的 。
悲观锁:总是假设最坏的情况 , 每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁 。比如Java里面的synchronized关键字的实现就是悲观锁 。
悲观锁适合写操作非常多的场景 , 乐观锁适合读操作非常多的场景,不加锁会带来大量的性能提升 。
悲观锁在Java中的使用,就是利用各种锁 。
乐观锁在Java中的使用 , 是无锁编程,常常采用的是CAS算法 , 典型的例子就是原子类,通过CAS自旋实现原子操作的更新 。
独享锁/共享锁
 独享锁是指该锁一次只能被一个线程所持有 。
 共享锁是指该锁可被多个线程所持有 。
 对于Java ReentrantLock而言,其是独享锁 。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁 。
 读锁的共享锁可保证并发读是非常高效的,写的过程是互斥的 。
 对于synchronized而言,当然是独享锁 。
互斥锁/读写锁
 独享锁/共享锁就是一种广义的说法 , 互斥锁/读写锁就是具体的实现 。
 互斥锁在Java中的具体实现就是ReentrantLock 。
 读写锁在Java中的具体实现就是ReadWriteLock 。
可重入锁
 可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁 。说的有点抽象 , 下面会有一个代码的示例 。
对于Java ReetrantLock而言,从名字就可以看出是一个重入锁,其名字是Reentrant Lock 重新进入锁 。


推荐阅读