这里主要是讲锁的概念,这14种锁在概念范畴上可能有重叠,并不是完全的14种。
1.CAScompare and set (比较更新,如果数据版本一致则更新否则不更新) 通过调用原子性汇编指令来实现原子性 *** 作。如java中的AtomicLong,AtomicInteger等就是基于CAS实现。
2.偏向锁加锁和解锁不需要额外消耗,和非同步方法仅纳秒级差距。适用于单线程或几乎无竞争的场景,如果竞争会升级为重量级锁带来额外消耗。
3.轻量级锁竞争的线程不会阻塞,而是轮询访问。优点是响应快,缺点是消耗CPU。
4.重量级锁竞争的线程会阻塞,优点是不消耗CPU,缺点是响应慢,适用于高吞吐量。
5.自旋锁自旋锁即通过循环不断地调用CAS指令来实现原子性 *** 作。自旋锁不改变线程状态,所以响应速度较快,但当并发量大时性能下降明显,因为其轮询时占用CPU。AtomicLong就是使用自旋锁的例子。
6.阻塞锁阻塞锁即让线程进入阻塞状态以等待获取锁。synchronized就是阻塞锁,Lock既提供阻塞锁,又提供非阻塞锁。
阻塞锁的优点是等待时不占用CPU,但进入锁和释放锁要比自旋锁慢。竞争激烈的情况下阻塞锁的性能要高于自旋锁。
7.可重入锁可重入锁(递归锁)即同一线程外层方法获取到锁之后调用内层方法是可以再次获取到锁并且不会产生死锁。(不可重入锁也称非递归锁)。
8.读写锁ReentrantLock等是排他锁,在同一时刻只允许一个线程访问。读写锁在同一时刻允许多个读线程访问,一个写线程访问。读写锁通过分离读锁和写锁减少了锁的竞争。
JDK中ReentrantReadWriteLock基于AQS实现了可重入式读写锁。
9.乐观锁乐观锁总是乐观地认为这个数据在同一时刻没有人修改。
乐观锁的实现基于CAS(Check And Set),先检查当前数据版本再更新。
特点:响应速度较快,并发量大时性能较差。
应用:
atomic系列都是乐观锁的体现
数据库版乐观锁:update t set n= newn, v = oldv+1 where v = oldv
10.悲观锁悲观锁认为数据在同一时刻总有人在修改。
悲观锁是先获取锁,再更新。
缺点:竞争线程会阻塞,响应较慢
优点:阻塞线程不消耗CPU,适用于高吞吐量
11.公平锁公平锁按线程请求锁的先后顺序来分配锁,即FIFO。公平锁要进行上下文切换,性能比非公平锁较差,除非业务需要公平性,一般不推荐使用。
12.非公平锁非公平锁是随机抢占机制。不需要进行上下文切换,性能比公平锁较好。
13.死锁死锁是两个以上的线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候。
例如,如果线程1锁住了A,然后尝试对B进行加锁,同时线程2已经锁住了B,接着尝试对A进行加锁,这时死锁就发生了。由于线程1处在等待线程2释放锁B期间而不肯释放已占有的锁A,由于线程1和线程2互相等待对方释放锁而自身又不释放锁,因此他们将永远阻塞下去,这就造成了死锁。
避免死锁:
1)加锁顺序:当多个线程访问多个锁时,如果访问的锁存在交集,则应使用相同的加锁顺序,否则可能导致死锁。
2)加锁时限:在尝试获取锁时设置超时时间,超时未能全部获取到所需锁则释放已获取的锁,随机等待然后重试。在等待的时间里别的线程就有机会获取交集中的已释放的锁,提高获取锁的成功率。
3)死锁检测:死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁和锁超时也不可行的场景。通过对访问锁的线程、访问的锁、已**的锁作标记然后检测是否存在互相等待对方释放锁的场景,如果存在则死锁发生了。此时应该:让一方的线程释放所有锁,随机等待然后重试;更好的方案是给一部分线程设置随机的优先级,让优先级低的那些线程回退,从而避免死锁。
转载自 http://www.360doc.com/content/18/0607/13/36490684_760396154.shtml
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)