【大厂Java并发编程面试题解】显式锁(Explicit Locks)(下)

简介: Java5之前只能用synchronized和volatile,Java5后Doug Lea提供了ReentrantLock,并非为了替代内置锁,而是当内置锁的机制不适用时,作为一种可选择的高级功能。 内置锁不适用的场景包括: 无法中断一个正在等待获取锁的线程 无限的锁等待 内置锁必须放在代码块里(编程有些局限性) 所以提供了J.U.C的Lock接口及实现。

4 性能考虑因素

Java5的时候J.U.C的ReentrantLock锁竞争性能非常好,到了Java6使用了改进后的算法来管理内置锁,所以现在差不太多了,只好一点点

竞争性能的影响可伸缩性的关键要素:如果有越多的资源被耗费在锁的管理和线程调度上,那么应用程序得到的资源就越少,锁的实现方式越好,将需要越少的系统调用和上下文切换。

5 公平性

ReentrantLock默认创建非公平的锁,非公平指被阻塞挂起的线程(LockSupport.park)都在AQS的CLH队列中排队等待自己被唤醒。他们是按照发出的请求顺序来排队的,但一旦有一个唤醒的就会和新来的线程竞争锁,新来的可能会“插队”。若新来的成功获取锁,那么它将跳过所有等待线程而开始执行,这意味着本该被唤醒的线程失败了,对不起您回到队列的尾部继续等。


一般,非公平锁的性能要好于公平锁。

因为一个线程被唤醒是需要时间的,挂起线程和唤醒恢复线程都存在开销,这个空隙如果有其他线程处于ready状态,无需上下文切换,那么直接运行就行。


A持有锁,B请求,但B在恢复的过程中,C可以插队"非公平"的获取锁,然后执行再释放,这时候B刚刚好做完上下文切换可以执行,这个对于B和C来说是一个“双赢”的局面,是提高吞吐量的原因。


JVM也没有在其内置锁上采用公平性的机制。

6 选型

除非使用到3提到的高级特性,或者内置锁无法满足需求时,否则还是老实用内置锁,毕竟是JVM自身提供的,而不是靠类库,因此可能会执行一些优化。


另外内置锁在利用kill -3 dump thread的时候可以发现栈帧上的一些monitor lock的信息,识别死锁,而J.U.C的锁这方面就不太行,当然JAVA6之后提供了管理和调试接口解决了。


7 读-写锁

ReentrantLock每次只有一个线程能持有锁,但是这种严格的互斥也会抑制并发。会抑制

  • 写/写
  • 写/读
  • 读/读

冲突,但是很多情况下读操作是非常多的,如果放宽加锁的需求,允许多个读操作可以同时访问数据,那么就可以提升性能。

但是要保证读取的数据是最新的,不会有其他线程修改数据

使用ReadWriteLock的场景:

  • 一个资源可以被多个读操作访问
  • 被一个写操作访问
  • 但二者不能同时进行

如果读线程正在持有锁,这时候另外一个写线程,那么会优先获取写锁:

public class ReadWriteMap<K, V> {
    private final Map<K, V> map;
    private final ReadWriteLock lock=new ReentrantReadWriteLock();
    private final Lock r=lock.readLock();
    private final Lock w=lock.writeLock();
    public ReadWriteMap(Map<K, V> map) {
        this.map=map;
    }
    public V put(K key, V value) {
        w.lock();
        try {
            return map.put( key, value );
        } finally {
            w.unlock();
        }
    }
    public V remove(Object key) {
        w.lock();
        try {
            return map.remove( key );
        } finally {
            w.unlock();
        }
    }
    public void putAll(Map<? extends K, ? extends V> m) {
        w.lock();
        try {
            map.putAll( m );
        } finally {
            w.unlock();
        }
    }
    public void clear() {
        w.lock();
        try {
            map.clear();
        } finally {
            w.unlock();
        }
    }
    public V get(Object key) {
        r.lock();
        try {
            return map.get( key );
        } finally {
            r.unlock();
        }
    }
    public int size() {
        r.lock();
        try {
            return map.size();
        } finally {
            r.unlock();
        }
    }
    public boolean isEmpty() {
        r.lock();
        try {
            return map.isEmpty();
        } finally {
            r.unlock();
        }
    }
    public boolean containsKey(Object key) {
        r.lock();
        try {
            return map.containsKey( key );
        } finally {
            r.unlock();
        }
    }
    public boolean containsValue(Object value) {
        r.lock();
        try {
            return map.containsValue( value );
        } finally {
            r.unlock();
        }
    }
}
目录
相关文章
|
3月前
|
算法 Java
50道java集合面试题
50道 java 集合面试题
|
5月前
|
缓存 Java API
Java 面试实操指南与最新技术结合的实战攻略
本指南涵盖Java 17+新特性、Spring Boot 3微服务、响应式编程、容器化部署与数据缓存实操,结合代码案例解析高频面试技术点,助你掌握最新Java技术栈,提升实战能力,轻松应对Java中高级岗位面试。
483 0
|
5月前
|
Java 数据库连接 数据库
Java 相关知识点总结含基础语法进阶技巧及面试重点知识
本文全面总结了Java核心知识点,涵盖基础语法、面向对象、集合框架、并发编程、网络编程及主流框架如Spring生态、MyBatis等,结合JVM原理与性能优化技巧,并通过一个学生信息管理系统的实战案例,帮助你快速掌握Java开发技能,适合Java学习与面试准备。
255 2
Java 相关知识点总结含基础语法进阶技巧及面试重点知识
|
3月前
|
算法 Java
50道java基础面试题
50道java基础面试题
|
5月前
|
缓存 Java 关系型数据库
Java 面试经验总结与最新 BAT 面试资料整理含核心考点的 Java 面试经验及最新 BAT 面试资料
本文汇总了Java面试经验与BAT等大厂常见面试考点,涵盖心态准备、简历优化、面试技巧及Java基础、多线程、JVM、数据库、框架等核心技术点,并附实际代码示例,助力高效备战Java面试。
200 0
|
5月前
|
缓存 Cloud Native Java
Java 面试微服务架构与云原生技术实操内容及核心考点梳理 Java 面试
本内容涵盖Java面试核心技术实操,包括微服务架构(Spring Cloud Alibaba)、响应式编程(WebFlux)、容器化(Docker+K8s)、函数式编程、多级缓存、分库分表、链路追踪(Skywalking)等大厂高频考点,助你系统提升面试能力。
278 0
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!