一、偏向锁,轻量级锁,重量级锁
这三种锁特指 synchronized 锁的状态,通过java对象的头mark world 来标识锁状态。
偏向锁 有时候我们加锁了,但是实际上却不存在竞争,所以没必要上锁,只要打个标识即可,这就是偏向锁的思想。如果一个对象初始化后,还没有任何线程来访问它,它就是可偏向的,第一个线程来访问它的时候,就把这个线程记录下来,如果下次仍然是这个线程访问,因为它是这个偏向锁的拥有者,所以直接获取锁,开销很小。
轻量级锁 如果synchronized 是被java的多个线程交替访问,不是同时竞争的,或者竞争的时间短,用CAS(Compare And Set)方式来轮询获取锁,不用经过上下文切换,这种时候没必要用重量级锁。
轻量级锁指原来是偏向锁,这时候一个非偏向锁拥有者的线程来访问对象,那么这个偏向锁就升级为轻量级锁,即通过轮询方式来获取锁,不用阻塞。
重量级锁 对于竞争比较激烈的场景,采用轻量级锁就需要等待很长时间的空转,这时候适合用重量级锁。它利用重量级锁的同步机制实现,开销比较大。
锁升级
总结: 偏向锁性能最好不用CAS操作,轻量级锁利用CAS和自旋避免重量级操作,性能次之;重量级锁利用系统实现,需要上下文切换,最终,性能最差。
二、可重入锁/非可重入锁
可重入锁即获取锁的线程,不释放锁的情况下,可以再次获取这个锁。非可重入锁即线程获取锁之后,只能释放了锁之后,才能再次获取锁。
ReentrantLock 即可重入锁。
三、共享锁、独占锁
共享锁是可以同时被多个线程同时占有的锁,独占锁即只能被一个线程所占有。我们常用的读写锁,读锁属于共享锁,可以同时被多个线程占有,写锁属于独占锁,只能被一个线程所占用。
四、公平锁、非公平锁
如果锁已经被占用,后续要获取锁的线程就会等待,开始排队,如果锁被释放后,等待时间最长的线程获取锁,这就是公平锁,非公平锁会在一定情况下准许插队的情况。
五、悲观锁和乐观锁
悲观锁即在获取资源之前,先获取锁,然后就进行操作,这样保证其他想操作的线程因为没有获取锁,所以无法操作。乐观锁,倾向于认为竞争不激烈,它不要求操作资源前先获取锁,而是直接用CAS操作,即更新的时候判断值是不是原来获取的值,如果是就直接修改(判断和更改是原子操作),如果不是就重试,在不独占的时候就完成了资源的修改。
六、自旋锁和非自旋锁
自旋锁的理念是如果线程拿不到锁,不会阻塞或释放 CPU 资源而是采用循环等待的方式,不断获取锁,这种方式即为自旋,我理解即为不停止循环判断锁是否释放了;非自旋锁,如果没有获取锁,则会进入阻塞或做其他事情。
七、可中断锁和不可中断锁
java中synchronized 关键字修饰的锁为不可中断锁,一旦线程申请了锁,需要等获取锁之后执行完毕逻辑后,不可以被中断。 ReentrantLock 即可中断锁,在获取锁的过程中,可以被中断,不用必须等待其他线程释放锁后,再获取锁。