悲观锁?
总是假设最坏的情况,每次去拿数据的时候都会认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(贡献资源每次只给一个线程使用,其他线程阻塞,用完之后再把资源转让给其他线程) Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现
乐观锁?
总是假设最好的情况,每次去拿数据都认为别人不会修改,所以不会上锁,但是会在更新的时候判断一下在此期间别人有没有更改数据。使用场景为CAS,原子类,数据库版本号,时间戳
两种锁的应用场景?
两种锁各有优缺点,乐观锁适用于写比较少的情况(多读的场景)。但如果是多写的场景,一般会经常产生冲突,这个时候使用悲观锁就比较合适
CAS算法?
compare and swap 或者compare and set算法,一种有名的无锁算法,在不使用锁的情况下实现了多线程之间的变量同步,即在没有阻塞的情况下实现了变量的同步,所以也叫非阻塞同步。CAS涉及到三个操作数:V A B v是需要读取的内存值 A是进行比较的值,B是要写入的值,只有当A和V相等时才会把B的值写入到V,否则进行个自旋操作
乐观锁(CAS)的缺点?
可能会出现aba问题,怎么解决?
通过版本号(时间戳)来解决ABA问题的,我们也可以使用版本号(verison)来解决ABA。
即乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1
操作,否则就执行失败。
循环时间长开销大:自旋CAS(不成功一直循环知道成功)如果长时间不成功,会给CPU带来非常大的执行开销。
只能保证一个共享变量的原子操作:CAS只对单个共享变量有效,当操作涉及跨多个共享变量时CAS无效,jdk1.5开始提供了AtomicReference类来保证对象之间的原子性,可以把多个变量放到一个对象里来进行CAS操作,所以我们可以使用锁或者利用AtomicReference类把多个共享变量合并成一个共享变量来操作
CAS与synchronized的使用情景?
CAS适用于写比较少的情况,synchronized使用于写比较多的情况
1.对于资源竞争较少(线程冲突较轻)的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户内核态之间的切换操作额外消耗cpu资源,而CAS基于硬件实现,不需要进入内核,不需要线程切换,操作自旋较少,因此可以获得更高的性能
2.对于资源竞争严重的情况,CAS自旋的概率大,会浪费更多的cpu资源,效率低于synchronized
补充:synchronized在jdk1.6以后进行了各种优化,在线程冲突较少的情况下,可以获得与CAS类似的性能,而在线程冲突严重的情况下,性能远高于CAS
死锁的条件?
1)互斥条件 : 一个资源每次只能被一个进程使用
2)请求与保持条件: 一个进程因请求资源而阻塞时,对已获得的资源保持不放
3)不剥夺条件 :进程已获得的资源,在未使用完之前,不能强行剥夺
4)循环等待条件: 若干进程之间形成一种头尾相接的循环等待资源关系
死锁的解决方法?
1.通过协议来预防或避免死锁,确保系统不会进入死锁状态
2.可以允许系统进入死锁状态,然后检测它,并修复
3.忽视这个问题,认为死锁不可能在系统内发生