// 写锁 释放rwLock.writeLock.unlock;

Java中的读写锁:ReentrantReadWriteLock。

6. 公平锁

java 可重用锁 java 常用锁_公平锁

公平锁是一种思想: 多个线程按照申请锁的顺序来获取锁。在并发环境中,每个线程会先查看此锁维护的等待队列,如果当前等待队列为空,则占有锁,如果等待队列不为空,则加入到等待队列的末尾,按照 FIFO 的原则从队列中拿到线程,然后占有锁。

7. 非公平锁

java 可重用锁 java 常用锁_java 锁 访问普通方法_02

非公平锁是一种思想: 线程尝试获取锁,如果获取不到,则再采用公平锁的方式。多个线程获取锁的顺序,不是按照先到先得的顺序,有可能后申请锁的线程比先申请的线程优先获取锁。

优点 :非公平锁的性能高于公平锁。

缺点 :有可能造成线程饥饿(某个线程很长一段时间获取不到锁)。

Java 中的非公平锁: synchronized是非公平锁。 ReentrantLock通过构造函数指定该锁是公平的还是非公平的,默认是非公平的。

8. 共享锁

java 可重用锁 java 常用锁_Java_03

共享锁是一种思想: 可以有多个线程获取读锁,以共享的方式持有锁。和乐观锁、读写锁同义。

Java 中用到的共享锁: ReentrantReadWriteLock。

9. 独占锁

java 可重用锁 java 常用锁_java 可重用锁_04

独占锁是一种思想: 只能有一个线程获取锁,以独占的方式持有锁。和悲观锁、互斥锁同义。

Java 中用到的独占锁: synchronized、 ReentrantLock。

10. 重量级锁

java 可重用锁 java 常用锁_公平锁_05

重量级锁是一种称谓:synchronized 是通过对象内部的一个叫做监视器锁(monitor)来实现的。监视器锁本身依赖底层的操作系统的 Mutex Lock 来实现。操作系统实现线程的切换需要从用户态切换到核心态,成本非常高。 这种依赖于操作系统 Mutex Lock来实现的锁称为重量级锁。为了优化 synchonized 引入了轻量级锁,偏向锁。

Java 中的重量级锁: synchronized。

11. 轻量级锁

java 可重用锁 java 常用锁_java 锁 访问普通方法_06

轻量级锁是 JDK6 时加入的一种锁优化机制:轻量级锁是在无竞争的情况下使用 CAS 操作去消除同步使用的互斥量。轻量级是相对于使用操作系统互斥量来实现的重量级锁而言的。轻量级锁在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。如果出现两条以上的线程争用同一个锁的情况,那轻量级锁将不会有效,必须膨胀为重量级锁。

优点 : 如果没有竞争,通过 CAS 操作成功避免了使用互斥量的开销。

缺点 :如果存在竞争,除了互斥量本身的开销外,还额外产生了 CAS 操作的开销,因此在有竞争的情况下,轻量级锁比传统的重量级锁更慢。

12. 偏向锁

java 可重用锁 java 常用锁_java 可重用锁_07

偏向锁是 JDK6 时加入的一种锁优化机制:在无竞争的情况下把整个同步都消除掉,连 CAS 操作都不去做了。偏是指偏心,它的意思是这个锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁一直没有被其他的线程获取,则持有偏向锁的线程将永远不需要再进行同步。持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何同步操作(例如加锁、解锁及对 Mark Word 的更新操作等)。

优点 :把整个同步都消除掉,连 CAS 操作都不去做了,优于轻量级锁。

缺点 :如果程序中大多数的锁都总是被多个不同的线程访问,那偏向锁就是多余的。

13. 分段锁

java 可重用锁 java 常用锁_java 锁 访问普通方法_08

分段锁是一种机制:最好的例子来说明分段锁是 ConcurrentHashMap。

ConcurrentHashMap 原理:它内部细分了若干个小的 HashMap,称之为段(Segment)。默认情况下一个 ConcurrentHashMap 被进一步细分为 16 个段,既就是锁的并发度。如果需要在 ConcurrentHashMap 添加一项 key-value,并不是将整个 HashMap 加锁,而是首先根据 hashcode 得到该 key-value 应该存放在哪个段中,然后对该段加锁,并完成 put 操作。在多线程环境中,如果多个线程同时进行 put 操作,只要被加入的 key-value 不存放在同一个段中,则线程间可以做到真正的并行。

线程安全:ConcurrentHashMap 是一个 Segment 数组, Segment 通过继承ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。

14. 互斥锁

java 可重用锁 java 常用锁_java 可重用锁_09

互斥锁与悲观锁、独占锁同义,表示某个资源只能被一个线程访问,其他线程不能访问。

读读互斥;

读写互斥;

写读互斥;

写写互斥。

Java 中的同步锁: synchronized。

15. 同步锁

java 可重用锁 java 常用锁_加锁_10

同步锁与互斥锁同义,表示并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。

Java中的同步锁: synchronized。

16. 死锁

java 可重用锁 java 常用锁_java 可重用锁_11

死锁是一种现象:如线程 A 持有资源 x,线程 B 持有资源 y,线程 A 等待线程 B 释放资源 y,线程 B 等待线程 A 释放资源 x,两个线程都不释放自己持有的资源,则两个线程都获取不到对方的资源,就会造成死锁。

Java 中的死锁不能自行打破。所以线程死锁后,线程不能进行响应。所以一定要注意程序的并发场景,避免造成死锁。

17. 锁粗化

java 可重用锁 java 常用锁_公平锁_12

锁粗化是一种优化技术:如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作都是出现在循环体体之中,就算真的没有线程竞争,频繁地进行互斥同步操作将会导致不必要的性能损耗。所以就采取了一种方案:把加锁的范围扩展(粗化)到整个操作序列的外部,这样加锁解锁的频率就会大大降低,从而减少了性能损耗。

18. 锁消除

java 可重用锁 java 常用锁_java 可重用锁_13

锁消除是一种优化技术: 就是把锁干掉。当 Java 虚拟机运行时发现有些共享数据不会被线程竞争时就可以进行锁消除。

那如何判断共享数据不会被线程竞争?

利用 逃逸分析技术:分析对象的作用域,如果对象在 A 方法中定义后,被作为参数传递到B方法中,则称为方法逃逸;如果被其他线程访问,则称为线程逃逸。

在堆上的某个数据不会逃逸出去被其他线程访问到,就可以把它当作栈上数据对待,认为它是线程私有的,同步加锁就不需要了。

19. synchronized

java 可重用锁 java 常用锁_java 可重用锁_14

synchronized 是 Java中的关键字:用来修饰方法、对象实例。属于独占锁、悲观锁、可重入锁、非公平锁。

作用于实例方法时,锁住的是对象的实例(this);

当作用于静态方法时,锁住的是类(class)。相当于类的一个全局锁,会锁所有调用该方法的线程;

synchronized 作用于一个非 null 的对象实例时,锁住的是所有以该对象为锁的代码块。它有多个队列,当多个线程一起访问某个对象监视器的时候,对象监视器会将这些线程存储在不同的容器中。

每个对象都有个 monitor 对象, 加锁就是在竞争 monitor 对象。代码块加锁是在代码块前后分别加上 monitorenter 和 monitorexit 指令来实现的,方法加锁是通过一个标记位来判断的。

20. Lock 和 synchronized 的区别

java 可重用锁 java 常用锁_java 锁 访问普通方法_15

Lock 是 Java 中的接口,可重入锁、悲观锁、独占锁、互斥锁、同步锁。

Lock 需要手动获取锁和释放锁。就好比自动挡和手动挡的区别;

Lock 是一个接口,而 synchronized 是 Java 中的关键字。synchronized 是 Java 语言内置的实现;

synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而 Lock 在发生异常时,如果没有主动通过 unLock 去释放锁,则很可能造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁;

Lock 可以让等待锁的线程响应中断,而 synchronized 却不行。使用 synchronized 时,等待的线程会一直等待下去,不能够响应中断;

通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到;

Lock 可以通过实现读写锁提高多个线程进行读操作的效率。

synchronized 的优势:

足够清晰简单,只需要基础的同步功能时,用 synchronized;

Lock 应该确保在 finally 块中释放锁。如果使用 synchronized,JVM确保即使出现异常,锁也能被自动释放;

使用 Lock 时,Java 虚拟机很难得知哪些锁对象是由特定线程锁持有的。

21. ReentrantLock 和 synchronized 的区别

java 可重用锁 java 常用锁_公平锁_16

ReentrantLock 是 Java 中的类:继承了 Lock 类,可重入锁、悲观锁、独占锁、互斥锁、同步锁。

划重点

相同点:

主要解决共享变量如何安全访问的问题;

都是可重入锁,也叫做递归锁,同一线程可以多次获得同一个锁;

保证了线程安全的两大特性:可见性、原子性。

不同点:

ReentrantLock 就像手动汽车,需要显示的调用 lock 和 unlock 方法, synchronized 隐式获得释放锁;

ReentrantLock 可响应中断, synchronized 是不可以响应中断的,ReentrantLock 为处理锁的不可用性提供了更高的灵活性;

ReentrantLock 是 API 级别的, synchronized 是 JVM 级别的;

ReentrantLock 可以实现公平锁、非公平锁,默认非公平锁。synchronized 是非公平锁,且不可更改;

ReentrantLock 通过 Condition 可以绑定多个条件。

Redis 分布式锁的正确实现方式( Java 版 )

为什么 ConcurrentHashMap 的读操作不需要加锁?

一句话撸完重量级锁、自旋锁、轻量级锁、偏向锁、悲观、乐观锁等各种锁 ---- 不看后悔系列 返回搜狐,查看更多