1. 首页 > 快讯

入职字节跳动必会的5道Java面试题!



介绍一下Java的锁升级?

在Java中,锁升级通常指的是从偏向锁(Biased Locking)到轻量级锁(Lightweight Locking),再到重量级锁(Heavyweight Locking)的过程。这个升级过程是Java虚拟机(JVM)在运行时根据锁的竞争情况自动进行的,目的是为了在不同的竞争环境下提供最合适的锁策略,从而提高程序的执行效率

  • 偏向锁(Biased Locking):

    • 偏向锁是JVM为了减少无竞争环境下的锁开销而引入的。当一个线程第一次访问某个对象的同步代码块时,JVM会将这个锁标记为偏向锁,并将锁对象指向该线程。如果后续的访问都是由同一个线程进行,那么这个线程就不需要进行同步操作,因为锁已经是偏向它的。

    • 偏向锁在没有竞争的情况下可以提高性能,但如果其他线程尝试访问同一个对象的同步代码块,就会触发偏向锁的撤销,并升级到轻量级锁。

    轻量级锁(Lightweight Locking):

    • 当一个线程尝试获取偏向锁,但发现锁已经被其他线程持有时,JVM会尝试将锁升级为轻量级锁。轻量级锁使用对象的Mark Word来存储锁信息,避免了传统重量级锁的开销。

    • 轻量级锁适用于有少量竞争的情况。如果轻量级锁被多个线程竞争,JVM会尝试自旋几次,如果自旋失败,锁将升级为重量级锁。

  • 重量级锁(Heavyweight Locking):

  • 当轻量级锁无法解决竞争问题时,JVM会将锁升级为重量级锁。重量级锁使用操作系统的互斥量(mutex)来实现,这会带来更大的开销,但也是最安全的锁策略。

    重量级锁通常用于高竞争环境下,它可以保证同步操作的安全性,但会降低程序的并发性能。

锁升级的过程是JVM自适应的,它会根据运行时的锁竞争情况来决定使用哪种锁策略。这种自适应机制使得Java程序能够在不同的运行环境下获得较好的性能表现。然而,锁升级也可能导致性能问题,特别是在锁升级过程中的开销可能会影响程序的执行效率。因此,合理设计同步代码块,减少锁的竞争,是提高Java程序性能的关键。

介绍一下Redis的Zset?

Redis 的ZSET(Sorted Set,有序集合)是 Redis 提供的一种数据结构,它能够存储不重复的字符串元素,并且每个元素都关联了一个分数(score)。ZSET 通过分数对元素进行排序,使得我们可以快速地获取到指定分数范围内的元素集合。

ZSET 的内部实现原理主要基于以下两种数据结构:

  1. 跳跃表(Skip List):

    • 从 Redis 3.2.0 版本开始,ZSET 的底层实现由原来的字典(Hash Table)和有序集合(Zipper List)变为了跳跃表。

    • 跳跃表是一种概率型数据结构,它允许快速地进行数据插入、删除和查找操作,并且可以有效地维护元素的有序性。

    • 跳跃表由多层链表组成,每一层都是有序的,并且每一层的元素都是前一层元素的子集。通过在多层链表中进行查找,可以快速跳过一些元素,从而提高查找效率。

  2. 哈希表(Hash Table):

    • 在使用跳跃表之前,ZSET 的底层实现是哈希表和有序链表(Zipper List)的组合。

    • 哈希表用于快速定位元素,而有序链表则用于维护元素的有序性。

    • 当元素数量较少时,使用有序链表可以提供较好的性能;但是当元素数量增多时,有序链表的性能会下降。

ZSET 的一些关键操作和它们在跳跃表实现中的原理如下:

  • 添加元素:将元素和其分数作为节点添加到跳跃表中,同时在哈希表中记录元素的存在。

  • 删除元素:从跳跃表中删除节点,并从哈希表中移除对应的记录。

  • 查找元素:通过哈希表快速定位元素,然后在跳跃表中进行查找。

  • 范围查询:利用跳跃表的有序性,可以快速地进行范围查询,获取指定分数范围内的元素。

  • 获取元素排名:跳跃表允许从两端开始遍历,可以快速地获取元素的排名。

使用跳跃表作为 ZSET 的底层实现,Redis 能够提供平均时间复杂度为 O(log N) 的插入、删除和查找操作,以及 O(M log N) 的范围查询操作(M 为查询结果中的元素数量)。这使得 ZSET 成为实现排行榜、实时更新的排行榜等功能的理想选择。、

cookie和session的区别?

  1. 存储位置:

    • Cookie:存储在客户端(用户的浏览器)上,由浏览器管理,可以设置过期时间,过期后自动删除。

    • Session:存储在服务器端,服务器使用一种叫做Session ID的机制来识别和管理不同的Session。Session ID通常存储在客户端的Cookie中,但也可以通过URL参数等方式传递。

  2. 存储大小:

    • Cookie:每个Cookie允许存储的数据量相对较小,大约为4KB。

    • Session:理论上没有大小限制,但是所有Session数据都存储在服务器上,如果用户量很大,会占用较多的服务器内存。

  3. 安全性:

    • Cookie:由于存储在客户端,容易被篡改或被第三方读取,因此安全性较低。可以通过设置HttpOnly属性来禁止JavaScript访问Cookie,增加安全性。

    • Session:由于存储在服务器端,只有通过Session ID才能访问,相对来说更安全。但是Session ID本身的安全性需要通过安全的传输方式(如HTTPS)来保证。

如何避免死锁?

  1. 避免嵌套锁定:

    • 避免在持有一个锁的情况下去请求另一个锁。如果需要同时访问多个资源,尽量一次性请求所有锁,或者使用某种形式的锁定顺序。

  2. 使用定时锁:

    • 使用tryLock()方法尝试获取锁,并设置一个超时时间。如果在指定的时间内无法获取锁,则放弃并重试或采取其他措施。

  3. 锁顺序:

    • 定义一个全局的锁顺序,并确保所有线程都按照这个顺序来请求锁。

  4. 锁超时:

    • 为锁请求设置超时时间。如果超时,则释放所有已持有的锁,并在稍后重试。

  5. 使用锁分离:

    • 将资源分解成更小的部分,使得每个线程需要的锁更少,从而减少死锁的可能性。

  6. 使用非阻塞结构:

    • 考虑使用非阻塞数据结构和算法,例如使用java.util.concurrent包中的并发集合。

  7. 检测死锁:

    • 使用工具和API来检测死锁,例如使用jconsole或jstack来分析线程的状态。

  8. 减少锁的持有时间:

    • 尽量减小持有锁的代码块,只在必要时才持有锁。

介绍一MySQL中的MVCC?

MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种用于数据库管理系统中的并发控制机制,它允许在不锁定资源的情况下执行读取和写入操作,从而提高数据库系统的并发性能。MVCC 通过为每个事务创建数据的快照来实现这一点,使得每个事务都能看到一致的数据视图,而不受其他并发事务的影响。

以下是 MVCC 的几个关键概念:

  1. 快照:

    • 每个事务在开始时都会获取一个数据库的快照,这个快照包含了事务开始时所有数据的版本。

  2. 版本链:

    • 数据库中的每条记录都可能存在多个版本,这些版本通过版本链(也称为版本历史)连接起来。每个版本都有一个时间戳或事务ID,用于标识它是在哪个事务中被创建或修改的。

  3. 读取操作:

    • 当一个事务执行读取操作时,它会读取与自己快照相对应的数据版本。这意味着即使数据在事务开始后被其他事务修改,当前事务也看不到这些修改。

  4. 写入操作:

    • 当一个事务执行写入操作时,它不会直接覆盖现有的数据版本。相反,它会创建一个新的数据版本,并将其添加到版本链的末尾。

end

本文采摘于网络,不代表本站立场,转载联系作者并注明出处:https://www.iotsj.com//kuaixun/3203.html

联系我们

在线咨询:点击这里给我发消息

微信号:666666