1. 内置锁
synchronized,这个不用说了,作用就是实现互斥之外,还有内存可见性,复合操作原子性等作用;一个线程拥有某个对象的synchronized锁,该线程调用wait方法后,会释放对象的锁;
2. ReentrantLock
ReentrantLock和synchronized在并发性和内存语义方面都相同,但是又多了锁投票,定时锁等候和中断锁等候
线程A和B都想获得对象O的锁,A获得锁后,如果是synchronized,线程B会一直等待直到获得锁为止;
而ReentrantLock会等待一段时间后停止等待;
ReentrantLock获得锁的三种方式
lock()方法:如果获得了锁,立即返回,否则处于休眠状态,直到获得锁为止;
tryLock()方法:如果获得了锁,返回true,否则,返回false;
tryLock(long timeout, TimeUnit unit): 等待timeout时间后还没有获得锁,则返回false,否则,返回true;
lockInterruptibly():如果获得了锁,立即返回;如果没有获得锁,该线程处于休眠状态直到获得锁,或者该线程被中断;
newCondition():作用和synchronized里的wait/notify方法很像;
Condition里面的await()方法和synchronized的wait()方法一样,Condition的signal()和notify()方法类似;
ReentrantLock和synchronized的区别:synchronized是在JVM层面实现的,当运行出错时,JVM会自动终止程序运行;而ReentrantLock是在JDK层面上实现的,需要手动释放锁,最后要在finally里手动释放;
结论:synchronized在竞争不是很激烈的时候适合使用,性能比ReentrantLock好一点点;但是竞争很激烈时,synchronized性能下降很厉害,而ReentrantLock能保持不变。
3. 自旋锁
3.1 为什么会出现自旋锁?
操作系统CPU进行线程的上下文替换需要时间比较长,所以我们想办法减少线程的上下文切换次数;自旋锁一般用于多处理器,线程A、B在不同的处理器上被执行,且都需要对象O的锁,现在O的锁被B占着,A请求O的锁,A不会进入阻塞状态,而是执行空循环等待B释放O的锁。
3.1 使用场景?
可知,如果B占用O很短,那么自旋锁效率很高,如果B会占用很长时间,或者该锁竞争激烈,效果不好。