Java并发编程之锁的艺术:面试与实战指南(一)

Java并发编程之锁的艺术:面试与实战指南(一)

文章目录

  • Java并发编程之锁的艺术:面试与实战指南(一)
    • 一、什么是锁?
    • 二、Java中有哪些类型的锁?
    • 三、synchronized 和 ReentrantLock的区别是什么?
    • 四、什么是乐观锁和悲观锁?
      • 悲观锁(Pessimistic Locking)
      • 乐观锁(Optimistic Locking)
    • 五、死锁是什么?如何避免?
    • 六、如何在Java中实现一个自定义的锁?
    • 七、什么是可重入锁(ReentrantLock)?
    • 八、什么是公平锁和非公平锁?

🌈你好呀!我是 山顶风景独好
💝欢迎来到我的博客,很高兴能够在这里和您见面!
💝希望您在这里可以感受到一份轻松愉快的氛围!
💝不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
🚀 欢迎一起踏上探险之旅,挖掘无限可能,共同成长!

一、什么是锁?

锁(Lock)是一种同步机制,用于控制多个线程对共享资源的访问。当一个线程需要访问某个共享资源时,它必须先获取该资源的锁,以确保在访问过程中其他线程不会同时访问该资源,从而避免数据的不一致性和其他并发问题。

二、Java中有哪些类型的锁?

  1. 乐观锁(Optimistic Locking):

    • 乐观锁认为一个线程去拿数据的时候不会有其他线程对数据进行更改,所以不会立即上锁。它会在数据更新时进行检查,如果数据在此期间没有被其他线程修改过,则更新成功;否则,操作失败。
    • 实现方式包括 CAS(Compare and Swap)机制、版本号 机制等。
  2. 悲观锁(Pessimistic Locking):

    • 悲观锁认为一个线程去拿数据时一定会有其他线程对数据进行更改,所以一个线程在拿数据的时候都会顺便加锁,这样别的线程此时想拿这个数据就会阻塞。
    • Java中的 synchronized关键字Lock的实现类 都是悲观锁的例子。
  3. 自旋锁(Spinlock):

    • 当一个线程尝试获取某个锁时,如果该锁已经被其他线程持有,则该线程不会立即阻塞,而是会采用循环的方式去尝试获取锁,直到获取到锁或者超过设定的最大循环次数。
    • 自旋锁适用于锁被持有的时间较短,且线程切换的开销较大的场景。
  4. 适应性自旋锁(Adaptive Spinlock):

    • 适应性自旋锁是自旋锁的一种优化形式。在获取锁的过程中,它会根据前一次获取锁的成功与否以及上一次自旋等待的时间等因素,动态地调整本次自旋等待的时间。
  5. 锁升级(Lock Escalation):

    • Java中的synchronized关键字在JVM层面进行了优化,包括锁升级机制。锁升级是指从一种锁状态逐渐过渡到另一种锁状态的过程,例如从偏向锁轻量级锁逐渐过渡到重量级锁
  6. 公平锁(Fair Lock)与非公平锁(Non-fair Lock):

    • 公平锁表示线程按照申请锁的顺序来获取锁,即等待时间最长的线程将优先获取锁。
    • 非公平锁则不保证等待时间最长的线程会先获得锁,有可能后申请的线程比先申请的线程优先获取锁。Java中的ReentrantLock可以通过构造函数指定是否为公平锁,默认是非公平锁。
  7. 可重入锁(Reentrant Lock):

    • 可重入锁允许同一个线程多次获取同一把锁而不会造成死锁。Java中的ReentrantLock就是可重入锁的一个实现。
  8. 独享锁(Exclusive Lock)与共享锁(Shared Lock):

    • 独享锁又称排它锁,同一时间只允许一个线程获取该锁,其他线程必须等待锁释放后才能获取。
    • 共享锁又称读锁,允许多个线程同时获取该锁进行读操作,但在读锁被释放之前,不允许其他线程进行写操作。
  9. 互斥锁(Mutex)与读写锁(Read-Write Lock):

    • 互斥锁是一种最简单的独享锁,同一时间只允许一个线程访问被保护的资源。
    • 读写锁则是对互斥锁的扩展,允许多个线程同时读取被保护的资源,但在读取过程中不允许其他线程进行写操作;而在写操作时,则不允许其他线程进行读或写操作。
  10. 分段锁(Segment Lock):

    • 分段锁是一种锁的设计思想,并不是具体的一种锁。它将一个大的数据结构(如数组或哈希表)分成多个小的段(Segment),每个段都有自己的锁。这样,多个线程可以并行地访问不同的段,从而提高了并发性能。例如,Java中的ConcurrentHashMap就采用了分段锁的设计。

三、synchronized 和 ReentrantLock的区别是什么?

  1. 获取锁的方式:

    • synchronized:是隐式锁,它在进入同步代码块或方法时自动获取锁,退出时自动释放锁。开发者无需显式地调用任何方法来获取或释放锁。
    • ReentrantLock:是显式锁,需要手动调用lock()方法获取锁,并在finally块中调用unlock()方法释放锁。这种方式提供了更大的灵活性,但也增加了出错的可能性,因为开发者必须确保在finally块中释放锁。
  2. 锁的公平性:

    • synchronized:是非公平锁,它并不保证等待时间最长的线程会先获得锁。
    • ReentrantLock:默认情况下也是非公平锁,但可以通过构造函数设置为公平锁。在公平锁的情况下,等待时间最长的线程会先获得锁。
  3. 功能丰富性:

    • synchronized:是Java内置的关键字,其功能相对较为简单,主要用于实现线程同步。
    • ReentrantLock:提供了比synchronized更丰富的功能。例如,它可以设置获取锁的超时时间,可以判断锁是否被其他线程持有,以及可以使用Condition类实现线程等待/通知机制等。
  4. 可重入性:

    • synchronized:是可重入的,这意味着一个线程可以多次获取同一把锁,而不会造成死锁。
    • ReentrantLock:同样是可重入的,与synchronized在这方面具有相同的行为。
  5. 中断响应:

    • synchronized:在获取锁的过程中,如果线程被中断,它会抛出InterruptedException异常,但不会释放锁。这可能导致死锁,因为其他等待锁的线程将无法获取锁。
    • ReentrantLock:提供了更灵活的中断响应。如果线程在等待锁的过程中被中断,它可以决定是继续等待、放弃等待还是响应中断。
  6. 性能:

    • 在高并发的情况下,ReentrantLock的性能可能会优于synchronized。但需要注意的是,synchronized的优化已经足够好,在许多场景下,其性能与ReentrantLock相当甚至更好。

四、什么是乐观锁和悲观锁?

悲观锁(Pessimistic Locking)

悲观锁认为并发操作之间发生冲突的可能性很高,因此,在数据被处理时,它会锁定资源以确保数据在处理过程中不会被其他事务修改。

  • 特点

    1. 悲观锁在数据被修改前就已经加锁,数据在被处理的过程中不会被其他事务读取或修改。
    2. 悲观锁的实现依赖于数据库提供的锁机制,如行锁、表锁等。
    3. 悲观锁适用于写操作频繁的场景,因为它可以避免脏读、不可重复读和幻读等并发问题。
  • 示例

    • 在SQL中,使用SELECT … FOR UPDATE语句可以对选定的行进行加锁,以确保在事务完成之前这些行不会被其他事务修改。
    • 在Java中,synchronized关键字和ReentrantLock等锁机制可以视为悲观锁的实现,因为它们会阻塞其他尝试访问共享资源的线程。

乐观锁(Optimistic Locking)

乐观锁认为并发操作之间发生冲突的可能性很小,因此它不会立即锁定资源,而是在数据提交更新时,检查数据是否被其他事务修改过。

  • 特点

    1. 乐观锁在数据被读取时不会加锁,而是在数据更新时检查版本号或时间戳等信息,以确保在读取到数据和提交更新之间的时间段内,数据没有被其他事务修改。
    2. 如果数据在读取到和提交更新之间被其他事务修改了,则更新操作会失败,需要采取重试或其他策略。
    3. 乐观锁适用于读操作频繁的场景,因为它可以减少加锁的开销,提高系统的并发性能。
  • 示例

    • 在数据库中,可以使用版本号或时间戳字段来实现乐观锁。在读取数据时,获取版本号或时间戳;在更新数据时,检查版本号或时间戳是否发生了变化,如果没有变化则更新数据并更新版本号或时间戳,否则认为数据已经被其他事务修改过,更新操作失败。
    • 在Java中,虽然没有直接的乐观锁实现类,但可以通过版本号、时间戳等机制在代码中实现乐观锁的逻辑。例如,在更新数据时,先读取数据的版本号,然后更新数据并更新版本号,最后提交更新时检查版本号是否发生了变化。

五、死锁是什么?如何避免?

  1. 避免嵌套锁:当需要加锁多个对象时,应将它们的锁顺序统一,尽量避免嵌套锁。
  2. 使用tryLock()方法:可以使用ReentrantLock类的tryLock()方法,在获取锁时设置超时时间,避免一直等待而产生死锁。
  3. 避免无限期等待:在获取锁时,应设置一个等待的超时时间,即一段时间后如果还没有获取到锁,就放弃任务执行。
  4. 使用不同的锁:如果可以使用不同的锁来代替原有的锁,那么可以尝试使用不同的锁来避免死锁。
  5. 尽量减少锁的持有时间:如果持有锁的时间过长,就会增加死锁的可能性,因此需要尽量减少锁的持有时间。
  6. 避免使用多个锁:在程序中避免使用多个锁,因为使用多个锁会增加死锁的可能性。可以采用一些技巧来避免使用多个锁,如采用粗粒度锁,将多个细粒度锁合并成一个大锁。
  7. 按照规定的顺序申请锁:为了避免死锁,可以规定一个申请锁的顺序,在申请锁的时候按照规定的顺序进行申请。
  8. 统一管理锁资源:将锁资源的管理进行统一管理,可以更好地避免死锁。
  9. 使用死锁检测工具:可以使用一些工具(如Java探针、Eclipse自带的死锁检测等)来检测和解决死锁问题。

六、如何在Java中实现一个自定义的锁?

通常可以通过实现java.util.concurrent.locks.Lock接口来完成。Lock接口定义了一些基本的锁操作方法,如lock(), unlock(), tryLock(), tryLock(long timeout, TimeUnit unit)等

以下是一个简单的自定义锁的实现示例:

import java.util.concurrent.locks.Lock;  public class CustomLock implements Lock {  private boolean isLocked = false;  private Thread lockedBy = null;  @Override  public void lock() {  Thread callingThread = Thread.currentThread();  while (isLocked && lockedBy != callingThread) {  // 等待锁被释放  try {  Thread.sleep(10); // 可以使用更复杂的等待策略  } catch (InterruptedException e) {  Thread.currentThread().interrupt(); // 恢复中断状态  throw new IllegalStateException("Interrupted while waiting for lock", e);  }  }  isLocked = true;  lockedBy = callingThread;  }  @Override  public void unlock() {  if (Thread.currentThread() != lockedBy) {  throw new IllegalMonitorStateException("Thread does not own lock");  }  isLocked = false;  lockedBy = null;  }  @Override  public boolean tryLock() {  if (!isLocked) {  isLocked = true;  lockedBy = Thread.currentThread();  return true;  }  return false;  }  @Override  public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {  long nanos = unit.toNanos(time);  long deadline = System.nanoTime() + nanos;  Thread callingThread = Thread.currentThread();  while (isLocked && lockedBy != callingThread) {  if (nanos <= 0) {  return false;  }  nanos = deadline - System.nanoTime();  // 使用nanos进行更精确的等待  Thread.sleep(nanos / 1000000, (int) (nanos % 1000000));  }  isLocked = true;  lockedBy = callingThread;  return true;  }  @Override  public Condition newCondition() {  throw new UnsupportedOperationException("Conditions not supported");  }  
}

七、什么是可重入锁(ReentrantLock)?

可重入锁(ReentrantLock),也称为递归锁,是一种支持同一个线程多次获取同一个锁的锁机制。在并发编程中,当一个线程获得了锁之后,如果再次尝试获取同一个锁时,可重入锁会允许该线程继续获取锁而不会被阻塞。这种机制允许线程在执行过程中多次获取同一个锁,并且在释放锁之前需要相同次数的解锁操作。

可重入锁的主要目的是解决在递归调用或嵌套代码中的锁定问题。当一个线程已经获得了锁,但在持有锁的代码块中又调用了另一个需要同样锁的方法时,如果使用非可重入锁,线程会因为无法再次获得同一个锁而陷入死锁状态。而可重入锁允许线程多次获得同一个锁,避免了死锁问题。

在Java中,ReentrantLock类是可重入锁的一种实现方式。这个类实现了Lock接口,提供了比内置锁(synchronized关键字)更多的灵活性和功能。ReentrantLock支持公平性设置,使得等待时间最长的线程优先获取锁。此外,ReentrantLock还提供了可中断的获取锁(lockInterruptibly()方法)和尝试获取锁(tryLock()方法)的功能,进一步增加了其灵活性。

使用可重入锁的场景包括递归函数、锁的嵌套、锁的互斥和锁的继承等,即任何需要在同一线程中多次获取同一把锁的场景,以及需要在方法调用链中多次获取同一把锁的场景。

八、什么是公平锁和非公平锁?

公平锁和非公平锁是两种类型的锁机制,它们的主要区别在于线程获取锁的顺序。

  • 公平锁:公平锁是指多个线程按照申请锁的顺序来获取锁。也就是说,如果一个线程比另一个线程早地请求了某个锁,那么在释放锁时,等待时间最长的线程(也就是最早请求锁的线程)会获得该锁。这种策略保证了线程间的公平性,但可能会导致整体的效率降低,因为线程需要等待更长的时间来获取锁。
  • 非公平锁:非公平锁则不保证线程获取锁的顺序。在释放锁时,任何等待的线程都有可能立即获得该锁,而不管它们等待时间的长短。这种策略可能导致某些线程长时间得不到锁,从而产生“饥饿”现象。但是,非公平锁的整体效率通常比公平锁高,因为线程在等待锁时不需要进行额外的排序或调度操作。

在Java中,ReentrantLock类提供了公平锁和非公平锁的实现。通过构造函数的参数,我们可以指定锁是公平的(true)还是非公平的(false)。默认情况下,ReentrantLock是非公平锁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/8365.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MinimogWP WordPress 主题下载——优雅至上,功能无限

无论你是个人博客写手、创意工作者还是企业站点的管理员&#xff0c;MinimogWP 都将成为你在 WordPress 平台上的理想之选。以其优雅、灵活和功能丰富而闻名&#xff0c;MinimogWP 不仅提供了令人惊叹的外观&#xff0c;还为你的网站带来了无限的创作和定制可能性。 无与伦比的…

后端常用技能:解决java项目前后端传输数据中文出现乱码、问号问题

0. 问题背景 最近做一个解析数据的小工具&#xff0c;本地运行时都正常&#xff0c;发布到服务器上后在导出文件数据时发现中文全部变成了问号&#xff0c;特此记录下问题解决的思路和过程 1. 环境 java 1.8 springboot 2.6.13 额外引入了fastjson&#xff0c;commons-csv等…

5.1 Java全栈开发前端+后端(全栈工程师进阶之路)-服务端框架-MyBatis框架-相信我看这一篇足够

0.软件框架技术简介 软件框架&#xff08;software framework&#xff09;&#xff0c;通常指的是为了实现某个业界标准或完成特定基本任务的软件组件规范&#xff0c;也 指为了实现某个软件组件规范时&#xff0c;提供规范所要求之基础功能的软件产品。 框架的功能类似于基础设…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷1(私有云)

#需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包…

XSKY SDS 6.4 重磅更新:NFS 性能飙升 3 倍,对象多站点等 10 多项功能强势升级

近日&#xff0c;XSKY星辰天合发布了 XSKY SDS V6.4 新版本&#xff0c;该版本在文件的性能提升、对象容灾能力完善方面改进异常显著&#xff0c;同时也大幅提高了存储系统的安全特性&#xff0c;适配更多的信创软硬件生态。 近来&#xff0c;软件定义存储&#xff08;SDS&…

【C++】map和set的基础详解

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

静态住宅代理 IP 的影响

在不断发展的在线业务和数字营销领域&#xff0c;保持领先地位势在必行。在业界掀起波澜的最新创新之一是静态住宅代理 IP 的利用。这些知识产权曾经是为精通技术的个人保留的利基工具&#xff0c;现在正在成为各行业企业的游戏规则改变者。 一、静态住宅代理IP到底是什么&…

背靠腾讯电商的视频号小店项目,怎么去操作呢?新手做店必看!

大家好&#xff0c;我是电商小V 视频号小店作为腾讯电商推出的电商项目&#xff0c;可以说现在就是处于爆火的状态&#xff0c;一直也是备受关注的&#xff0c;同时也是吸引了很多的玩家入驻&#xff0c;因为视频号小店就是一个新的平台&#xff0c;一个新的场地&#xff0c;现…

泸州知识付费系统,辅导班自己开还是加盟好?哪种好?

随着经济水平的提升&#xff0c;大家对自己知识要求越来越高&#xff0c;辅导行业也发展的越来越火爆&#xff0c;很多人看中了这个行业的发展&#xff0c;但是很多人不知道加盟好还是自己开培训机构好&#xff1f;今天大家一起聊聊。 一、成功的产品和运作模式降低失败风险 一…

软设之多种程序设计语言的特点

Fortran语言&#xff0c;用于科学计算 记忆口诀:for为了科学献身 Pascal语言&#xff0c;为教学而开发 记忆口诀:发音和毕加索相近&#xff0c;毕加索改行教育 Lisp语言:函数式程序语言&#xff0c;用于人工智能 记忆口诀:国外有个人工智能叫lisa c语言:指针操作能力强&a…

学习软考----数据库系统工程师25

关系规范化 1NF&#xff08;第一范式&#xff09; 2NF&#xff08;第二范式&#xff09; 3NF&#xff08;第三范式&#xff09; BCNF&#xff08;巴克斯范式&#xff09; 4NF&#xff08;第四范式&#xff09; 总结

在 Linux 中复制文件和目录

目录 ⛳️推荐 前言 在 Linux 命令行中复制文件 将文件复制到另一个目录 复制文件但重命名 将多个文件复制到另一个位置 复制时处理重复文件 交互式复制文件 在 Linux 命令行中复制目录 仅复制目录的内容&#xff08;不是目录&#xff09; 复制多个目录 测试你的知…

74从零开始学Java之排序算法中的冒泡和选择排序

作者:孙玉昌,昵称【一一哥】,另外【壹壹哥】也是我哦 CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者 前言 我们要想成为一个优秀的程序员,其实非常关键的一点就是要锻炼培养自己的编程思维,就好比一个狙击手,要通过大量的射击训练要用大量的子弹喂出来。同样的…

值得推荐的多款iPaaS工具

当今企业面临着日益复杂的数据和系统集成挑战&#xff0c;为了提高业务效率和灵活性&#xff0c;许多企业转向了iPaaS工具&#xff08;Integration Platform as a Service&#xff0c;即集成平台即服务&#xff09;。iPaaS工具可以帮助企业轻松地连接和集成各种应用程序、数据和…

Xinstall助力推广结算统计,让数据一目了然

在当今数字化营销的时代&#xff0c;推广活动的成功与否往往取决于精准的数据统计和分析。然而&#xff0c;对于许多广告主和开发者来说&#xff0c;推广结算统计却是一个令人头疼的问题。数据分散、渠道繁多、统计口径不一&#xff0c;这些问题都给推广效果的衡量带来了极大的…

SM3国密算法简介及应用案例

SM3国密算法简介及应用案例 随着信息技术的迅猛发展&#xff0c;数据安全已经成为全球各国关注的重点。为了加强数据加密的自主性与安全性&#xff0c;中国国家密码管理局&#xff08;SCA&#xff09;发布了一系列的国家密码标准算法&#xff0c;其中 SM3 是一种重要的国密&am…

Camtasia Studio 的功能介绍及常规操作教程

随着数字媒体时代的快速发展&#xff0c;视频已经成为人们获取信息、娱乐和沟通的主要方式之一。在这样的背景下&#xff0c;一款强大且易于使用的视频编辑和制作工具显得尤为重要。Camtasia Studio就是这样一款能够满足各种视频制作需求的优秀软件。 一、Camtasia Studio的特…

PostgreSQL <>运算符null值不会被包括在查询结果中的坑

文章目录 前言一、问题示例二、解决方法三、补充内容1. 使用 COALESCE 函数2. 使用 IS NULL 和 IS NOT NULL 运算符总结前言 在使用 PostgreSQL 进行查询时,我们经常会使用 <> 运算符来表示不等于。然而,需要注意的是,当涉及到 NULL 值时,<> 运算符不会将 NUL…

pandas处理excel问题(记录)

1. pandas读取excel合并单元格问题 网上查到的都是 df[col] df[col].ffill() 这个能解决大部分简单合并的问题&#xff0c;遇到复杂的就不行了遇到上图中 有空的情况&#xff0c;ffill() 也会向下填充。 所以不能通过这种方式 import openpyxl # 拆分所有的合并单元格&#…

如何注册新加坡公司

作为亚太地区的金融中心之一&#xff0c;新加坡一直以来都是企业布局海外市场的首选目的地。无论是开展新业务还是寻求职业发展&#xff0c;新加坡都是一个富有吸引力的选择。 新加坡公司注册资料 1、需提供拟进行注册的新加坡纯英文名称&#xff1b; 2、公司注册资本&#…