Java中的锁
不全是锁,有的指的是锁的特性,有的指锁的状态,有的指锁设计。
乐观锁
乐观的认为不加锁也是可以的,对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。
使用:乐观锁在 Java 中的使用,是无锁编程,适合读操作非常多的场景,常常采用的是 CAS 算法,典型的例子就是原子类,通过 CAS 自旋实现原子操作的更新。
悲观锁
认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。真正的实现加锁,认为不加锁是有问题的。
使用:悲观锁在 Java 中的使用,就是利用各种锁,悲观锁适合写操作非常多的场景。
可重入锁
又名递归锁,当一个线程在外层方法获取锁的时候,在进入内层方法会自动地获取锁。
ReentrantLock、synchronized都是可重入锁,在一定程度上避免死锁。
读写锁
里面维护两个锁的实现,一个是读锁,一个是写锁。
写锁一次只能有一个线程获得锁。读锁一次可以有多个线程获得锁。
写锁的优先级高于读锁。
分段锁
不是真正的锁,而是一种思想,用于将数据分段,并在每个分段上单独加锁,将锁的粒度分的更小,以提高并发率。
自旋锁
不断的尝试获取锁,不会使线程阻塞,提高效率,但会损耗CPU。
独占锁/共享锁
独占锁:也叫互斥锁,读写锁中的写锁,一次只允许一个线程获得锁。
共享锁:读写锁中的读锁,一次允许多个线程获得锁。
ReentrantLock、synchronized都是独占锁。
公平锁/非公平锁
公平锁:按照请求的顺序分配锁,拥有稳定获得锁的机会,但性能可能比非公平锁低。
非公平锁:不按照请求的顺序分配锁,不一定拥有获得锁的机会,但性能可能比公平锁高。
synchronized 而言,是一种非公平锁。
ReentrantLock 默认是非公平锁,但是底层可以通过 AQS 来实现线程调度,所以可以使其变成公平锁。
偏向锁/轻量级锁/重量级锁
为了优化synchronized,提高锁的获取与释放效率 ,在jdk 6之后提出了锁状态。
锁的状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。
偏向锁:只有一个线程访问一段同步代码块,此时会将线程的id存入对象头,下次该线程来获取锁的时候直接分配即可 ,降低获取锁的代价。
轻量级锁:当锁状态为偏向锁状态时,又有线程进行访问,那么锁状态升级为轻量级锁,不会让线程进入阻塞状态,而是自旋尝试获得锁,以提高效率。
重量级锁:当锁状态为重量级锁时,如果线程数量太多,会升级为重量级锁,线程进入阻塞状态,有操作系统调度分配。