java锁相关

本文解析了自旋锁、悲观锁和乐观锁的原理及其在并发控制中的应用,包括Synchronized、ReentrantLock和ReentrantReadWriteLock的实现,以及Java同步关键字的工作机制,如轻量级锁、重量级锁和偏向锁的转换过程。

自旋锁:为了不放弃CPU执行事件,循环的使用ACS技术对数据尝试进行更新,直至成功。

悲观锁:假定会发生并发冲突,同步所有对数据的相关操作,从读数据就开始上锁。

乐观锁:假定没有冲突,在修改数据时如果发现数据和之前获取的不一致,则读最新数据,修改为最新后重新修改数据。

独享锁(写):给资源加上写锁,线程可以修改资源,其他线程不能再加锁。(单个线程写)

共享锁(读):给资源加上读锁后只能读不能改,其他线程也只能加读锁,不能加写锁;(多读)。

可重入锁、不可重入锁:线程拿到一把锁之后,可以自由进入同一把锁所同步的其他代码。

公平锁、非公平锁:争抢锁的顺序,如果是按先来后到,则为公平。

几种锁的重要实现:Synchronized 、ReentrantLock、 ReentrantReadWriteLock

同步关键字Synchronized 

属于最基本的线程通信机制,基于对象监视器实现。

Java中的每个对象都与一个监视器相关联,一个线程可以锁定和解锁。

一次只有一个线程可以锁定监视器。

试图锁定修改监视器的任何线程都会被阻塞,直到它们可以获得该监视器上的锁定为止。

特性:可重入、独享、悲观锁。

锁的范围:类锁、对象锁、锁消除、锁粗化。

提示:同步关键字,不仅是实现同步,根据JVM规定还能保证可见性(读取最新主内存数据,结束后写入主内存)。

同步关键字加锁原理:

HotSpot中,对象前面会有一个类指针和标题,存储标识哈希码的标题以及用于分代垃圾收集的年龄和标记位。

轻量级加锁:直接改变一个对象的头信息, 修改轻量级锁内容,锁状态

使用CAS修改mark Word完毕,加锁成功。则mark work中的tag进入00 状态。

解锁的过程,则是一个逆向恢复mark word的过程。

默认情况下JVM会经历:偏向锁->轻量级锁->重量级锁这四个状态。

Tag锁状态标志:00:无锁、01轻量级锁CAS、10:重量级锁、监视器锁、11:GC

偏向锁到轻量级锁:

偏向锁: 对象头信息中有个是否开启偏向,并且记录线程ID,当只有一个线程使用并判断是否加锁,就会记录这个线程的id。后续这个线程再次访问,直接判断线程id即可。再次有个线程访问该对象,则偏向锁升级为轻量级锁

偏向标记第一次有用,出现过争抢后就没用了。 -XX: UserBiasedLocking 禁用使用偏置锁定。

偏向锁,本质就是无锁,如果没有发生过任何多线程争抢锁的情况,,JVM认为就是单线程,无需做同步(JVM就是为了少干活:同步在JVM底层是有很多操作来实现的,如果没出现争抢,就不需要去做同步操作)

同步关键字加锁原理- 轻量级锁:

使用CAS修改mark word完毕,加锁成功。则mark word中的tag进入00状态。解锁过程,则是一个逆向恢复mark woed的过程。

重量级锁 - 监视器(monitor)

修改mark word失败,会自旋CAS一定次数,该次数可以通过参数配置:超过次数,仍未抢到锁,则锁升级为重量级锁,进入阻塞。

Monitor也叫做管程,计算机操作系统原理中有提及类似概念。一个对象会有一个对应的monitor。

<think> 我们正在处理用户关于Java相关的笔试选择题的请求。根据引用[1]和引用[2]的内容,用户之前提供的引用都是关于Java面试题的选择题,但具体到的部分,引用中并没有直接给出。然而,我们可以根据Java的常见考点来创建一些典型的选择题。 Java相关的考点通常包括: - synchronized关键字 - ReentrantLock类 - 的类型(可重入、公平/非公平、读写等) - 死 - volatile关键字 - 的优化(如自旋消除、粗化等) 我们可以设计一些选择题,每个题目附上答案和解析。 下面提供几道常见的Java相关笔试选择题: --- ### **Java相关笔试选择题** #### 1. 关于`synchronized`关键字,以下说法错误的是() A. `synchronized`可以用于修饰实例方法 B. `synchronized`可以用于修饰静态方法 C. `synchronized`可以保证线程的可见性和原子性,但不能保证有序性 D. `synchronized`是可重入 <details> <summary>答案与解析</summary> 答案:C 解析: - A和B正确,`synchronized`可以修饰实例方法和静态方法,分别住当前实例对象和当前类的Class对象。 - C错误,`synchronized`可以保证原子性、可见性,同时也保证了有序性(因为一个线程在释放之前的所有操作对于随后获得的线程是可见的,且由于的互斥性,保证了有序性)。 - D正确,`synchronized`是可重入,同一个线程可以多次获取同一把。 因此,选项C是说法错误的。 </details> --- #### 2. 下列关于`ReentrantLock`和`synchronized`的区别,说法正确的是() A. `ReentrantLock`是关键字,而`synchronized`是类 B. `ReentrantLock`需要手动释放,而`synchronized`不需要 C. `ReentrantLock`只能是非公平,而`synchronized`可以是公平 D. `ReentrantLock`不能实现可重入,而`synchronized`可以 <details> <summary>答案与解析</summary> 答案:B 解析: - A错误,`ReentrantLock`是类,而`synchronized`是关键字。 - B正确,`ReentrantLock`需要显式调用`lock()`和`unlock()`方法,而`synchronized`在代码块执行完后会自动释放。 - C错误,`ReentrantLock`默认是非公平,但可以通过构造方法设置为公平;而`synchronized`是非公平。 - D错误,`ReentrantLock`和`synchronized`都是可重入。 因此,选项B正确。 </details> --- #### 3. 以下哪种情况不会引起死?() A. 线程A持有1并尝试获取2,线程B持有2并尝试获取1 B. 线程A持有1并尝试再次获取1 C. 线程A持有1并尝试获取2,同时线程B持有2并尝试获取3,线程C持有3并尝试获取1 D. 线程A循环等待线程B持有的,而线程B也在循环等待线程A持有的 <details> <summary>答案与解析</summary> 答案:B 解析: - 死产生的四个必要条件:互斥、请求与保持、不可剥夺、循环等待。 - A和D是典型的两个线程循环等待死;C是三个线程循环等待死。 - B是可重入的情况,同一个线程对同一个可以重入,不会发生死。 因此,选项B不会引起死。 </details> --- #### 4. 关于`volatile`关键字,以下说法正确的是() A. `volatile`能保证复合操作的原子性 B. `volatile`可以解决多线程环境下的所有并发问题 C. `volatile`可以保证变量的可见性和有序性 D. `volatile`变量的操作不需要加 <details> <summary>答案与解析</summary> 答案:C 解析: - A错误,`volatile`不能保证复合操作的原子性,比如`i++`操作。 - B错误,`volatile`不能解决所有并发问题,它只能保证可见性和有序性,不能保证原子性。 - C正确,`volatile`通过内存屏障保证可见性和禁止指令重排(有序性)。 - D正确,`volatile`变量操作不需要加,但要注意它不能保证原子性。 选项C和D都是正确的,但题目要求选择正确的是,并且D也是正确的,但C更全面地描述了`volatile`的特性。然而,本题是单选题,且通常C是更标准的答案(因为D虽然正确,但前提是原子操作,而复合操作则不行)。但严格来说,题目问的是“说法正确的是”,C和D都正确,但题目可能是单选题。因此,我们需要重新审视选项。 重新审视选项: - C:`volatile`可以保证变量的可见性和有序性(正确) - D:`volatile`变量的操作不需要加(正确,但是这里指的是对volatile变量的读写操作是原子的,所以单个读写操作不需要加,但复合操作还是需要) 由于题目是单选题,且C是`volatile`最核心的特性,而D虽然正确,但不够严谨(因为复合操作还是需要)。所以通常选择C。 但要注意:题目中并没有说单选题还是多选题,从格式上看是单选题(只有一个选项正确)。实际上,D也是正确的,但是题目要求选择正确的,而C是更本质的特性。我们再看其他选项,A和B错误,所以C和D正确,但题目可能是单选题,因此需要调整。 实际上,在Java中,`volatile`变量的读写操作都是原子的(包括long和double变量),所以单个读写操作不需要加。所以D正确。但是,题目要求选择正确的是,而C和D都正确,但我们只能选一个。因此,我们可能需要修改题目为单选题,所以选择最全面的选项。 然而,原题中并没有说明是多选题,所以按照单选题处理。实际上,很多笔试中,这道题的标准答案是C。因为D说法虽然正确,但不是`volatile`的完整特性,而C是它的两大特性(可见性和有序性)。同时,D的说法过于绝对,因为虽然不需要加,但并不能完全替代。 所以,我们选择C。 </details> --- #### 5. 下列哪个可以支持中断响应?() A. `synchronized` B. `ReentrantLock` C. 两者都支持 D. 两者都不支持 <details> <summary>答案与解析</summary> 答案:B 解析: - `synchronized`在等待的过程中无法被中断,只能一直等待。 - `ReentrantLock`提供了`lockInterruptibly()`方法,可以在等待的过程中响应中断。 因此,只有`ReentrantLock`支持中断响应,选项B正确。 </details> --- #### 6. 下面关于读写`ReentrantReadWriteLock`的描述,错误的是() A. 读是共享,写是独占 B. 读和写都支持重入 C. 写可以降级为读,读可以升级为写 D. 写被获取时,所有读的获取都必须等待 <details> <summary>答案与解析</summary> 答案:C 解析: - A正确,读写中,读是共享的,写是独占的。 - B正确,`ReentrantReadWriteLock`支持的重入。 - C错误,写可以降级为读(即先获取写,再获取读,然后释放写),但是读不能升级为写(因为可能会有多个读同时存在,升级为写需要独占,所以会导致死)。 - D正确,写被持有时,其他线程获取读和写都必须等待。 因此,选项C错误。 </details> --- #### 7. 下列哪种优化技术是通过避免线程在用户态和内核态之间切换来提高性能的?() A. 消除 B. 粗化 C. 自旋 D. 轻量级 <details> <summary>答案与解析</summary> 答案:C 解析: - 消除:编译器在运行时检测到不可能存在共享数据竞争的,将其消除。 - 粗化:将多个连续的操作合并为一个操作,减少请求次数。 - 自旋:当线程请求失败时,不立即阻塞,而是循环等待一段时间(自旋),避免用户态和内核态的切换开销。 - 轻量级:在无竞争的情况下使用CAS操作来避免使用重量级。 自旋通过自旋避免了线程切换(即用户态和内核态切换)的开销。所以答案是C。 </details> --- §§相关问题§§ 1. Java中`synchronized`和`ReentrantLock`在性能上有何差异? 2. 如何诊断和避免Java程序中的死? 3. `volatile`关键字和`synchronized`关键字在可见性上的区别是什么? </think> ### Java相关笔试选择题 以下是Java机制的常见笔试选择题(含答案与解析),结合Java并发编程核心知识点设计: --- #### 1. **关于`synchronized`关键字,说法正确的是()** A. `synchronized`可以修饰变量 B. `synchronized`静态方法住的是类实例对象 C. `synchronized`同步代码块支持可重入性 D. `synchronized`无法保证可见性和有序性 <details> <summary>答案与解析</summary> 答案:**C** 解析: - A错误:`synchronized`只能修饰方法或代码块,不能修饰变量。 - B错误:静态方法住的是类的`Class`对象,而非实例对象。 - C正确:同一线程可重复获取同一把(可重入特性)。 - D错误:`synchronized`通过`monitorenter/monitorexit`指令保证原子性、可见性和部分有序性[^1]。 </details> --- #### 2. **`ReentrantLock`与`synchronized`的主要区别是()** A. `ReentrantLock`是非阻塞 B. `ReentrantLock`必须手动释放 C. `synchronized`支持公平策略 D. `ReentrantLock`无法实现可重入 <details> <summary>答案与解析</summary> 答案:**B** 解析: - A错误:两者都是阻塞。 - B正确:`ReentrantLock`需显式调用`lock()`和`unlock()`;`synchronized`自动释放。 - C错误:`ReentrantLock`支持公平/非公平,`synchronized`仅非公平。 - D错误:`ReentrantLock`是可重入。 </details> --- #### 3. **以下可能引起死的代码场景是()** ```java // 线程1 synchronized (lockA) { synchronized (lockB) { /* ... */ } } // 线程2 synchronized (lockB) { synchronized (lockA) { /* ... */ } // 位置X } ``` A. 线程1先获得`lockA`,线程2先获得`lockB` B. 线程1和线程2同时执行位置X C. 线程1释放`lockB`后线程2再获取`lockA` D. 线程2使用`tryLock()`替代`synchronized` <details> <summary>答案与解析</summary> 答案:**A** 解析: - A正确:线程1持有`lockA`请求`lockB`,线程2持有`lockB`请求`lockA`,形成循环等待(死必要条件)。 - B错误:线程无法同时进入同步块。 - C错误:释放后不会死。 - D错误:`tryLock()`可避免死(尝试获取失败则放弃)。 </details> --- #### 4. **关于`volatile`关键字,说法错误的是()** A. 保证变量的可见性 B. 禁止指令重排序 C. 能替代`synchronized`实现原子操作 D. 适用于多线程读、单线程写的场景 <details> <summary>答案与解析</summary> 答案:**C** 解析: - A正确:强制线程从主内存读取最新值。 - B正确:通过内存屏障防止重排序。 - C错误:`volatile`不能保证复合操作(如`i++`)的原子性。 - D正确:典型用例如状态标志位[^2]。 </details> --- #### 5. **`ReadWriteLock`的特性是()** A. 写可被多个线程同时持有 B. 读释放前,写无法获取 C. 读与写互斥,读之间共享 D. 默认公平策略优于非公平策略 <details> <summary>答案与解析</summary> 答案:**C** 解析: - A错误:写是独占。 - B错误:写获取时,新读请求会被阻塞。 - C正确:读写互斥,读读共享(提高读密集型性能)。 - D错误:非公平策略吞吐量更高。 </details> ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值