今日面试题总结————并发篇

请解释一下volatile关键字?

volatile是Java中的轻量级的同步机制,总共有三个特性:可见性、非原子性、有序性(也叫禁止重排)

可见性:在Java内存模型中(JMM),多个线程访问主内存中的变量,将变量拷贝到自己的工作内存中。当然线程对变量的操作也算是对副本的操作,当其中一个线程改变了副本就会将改变后副本值更新到主内存中去,加了volatile后其他线程也能知道主内存中数据发生改变,从而自身副本也重新拷贝,反之不不知道已经改变。

非原子性:原子性就是不可分割,该做就一直做。在多线程环境下,多个线程竞争资源,预期值会和实际值有偏差,例如多个线程对主内存中副本数据进行加一操作,A线程进入后还没修改数据就被挂起,这时B进入修改了数据、这时A被唤醒,A中副本数据还没来的及修改就马上执行了+1操作,就造成两个线程执行后数据值一样,造成值覆盖。

有序性:因为很多时候我们写的java代码顺序和虚拟机执行的字节码文件顺序是不一致的,因为JVM会对其进行重优化。但是在高并发环境下这种重优化(重排序)会带来各种问题,单例模式下对象创建过程是:1、分配对象地址 2、初始化对象 3、让对象执向分配的地址 ,当高并发环境下发生的重排序可能会导致 132顺序的发生,从而使其还没有初始化就指向了分配地址,这就造成了判断出错。

 

AtomicInteger底层原理实现?什么是CAS?

AtomicInteger底层实现:由CAS确保原子性+Valatile

要想弄清楚AtomcInteger怎么实现的还是要先明白CAS。

CAS(Compare and Swap)的意思是比较和交换,使用的就是乐观锁的思想。CAS之所以可以保证原子性,是依赖于他里面的Unsafe类,这个类是在java.sum.muix包下的是native(本地)方法修饰的类,说明他是从娘胎中就由得类、是操作系统底层实现的类,它可以像c语言中的指针一样指向当前对象地址,所以通过Unsafe类的本地方法获取到对象的地址 ,然后通过本类对象+本类对象地址确定本类对象的值,然后再进行判断预期值和实际对象的值是否相等,如果相等就表明没有被修改,如果不相等就表明被修改了,就重新获取值再比较。

所以搞清楚了CAS不难搞懂AtomicInteger:附上源码及解释

 // setup to use Unsafe.compareAndSwapInt for updates(更新操作时提
供“比较并替换”的作⽤)
 private static final Unsafe unsafe = Unsafe.getUnsafe();
//偏移量,就是所谓的地址
 private static final long valueOffset;
 static {
 try {
//通过unsafe本地方法获取到地址
 valueOffset = unsafe.objectFieldOffset

 (AtomicInteger.class.getDeclaredField("value"));
 } catch (Exception ex) { throw new Error(ex); }
 }
//这里的value被指定为了volatile,所以在被修改后有可见性,value用于后面的比较并替换
 private volatile int value;

当然你会想说为什么我不用Synchronized而用这个CAS呢,因为Synchronized每次都只会让一个线程进入所以会导致系统的并发性下降、所以这里用到了CAS。当然CAS也会存在一些弊端:1、只允许一个对象的并发操作,如果是对个对象时就会无法保证原子性。2、多次循环空转浪费CPU内存 。3、会发生ABA问题。

ABA问题:当一个线程A执行速度更快,线程B执行速度更慢。A线程修改了多次主线程副本值,但是最终结果还是原来的值,B线程操作时,那本身副本值与预期值对比,发现一样是true执行,并不知道A线程已经多次修改了该值。这种问题就是ABA问题。

解决方案:使用原子引用阀值。通过原子引用阀值添加一个类似版本号信息的值,如果线程修改了一次该值就加一,不管最后修改值是否与原来一致。后面判断版本号与自己版本号是否一致即可。

 

 

解释一下为什么ArrayList、Map、Set都是线程不安全的(并发)?

因为在多线程环境下,多个线程竞争资源(例如操作数+1)时,可能随时会被其他线程抢去,这样在高并发环境之下很容易抛出并发修改异常(ConcurrentModificationException)。

解决方案:1、加Synchronized锁   2、使用Collections.SynchronizedList(new xxx); 3、使用写时复制(CopyOnWriterArrayXXx)、HashMap使用(CurrentHashMap)

12原理都是控制线程数。

3、CopyOnWriterArrayXXX:是给操作对象创建一个副本,当一个线程在进行读操作时,另一个想要进行写操作,那么就创建一个一模一样的对象供这个线程写、当写完后把地址引用指向修改后的副本,依次类推。

CurrentHashMap则是分段管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值