《面试1v1》synchronized

news2024/12/30 1:47:41

源码都背下来了,你给我看这

我是 javapub,一名 Markdown 程序员从👨‍💻,八股文种子选手。

面试官: 你好,我看到你的简历上写着你熟悉 Java 中的 “synchronized” 关键字。你能给我讲讲它的作用吗?

候选人: 当然,“synchronized” 是 Java 中的一个关键字,用于实现同步机制。它可以用来修饰方法或代码块,以确保在同一时间只有一个线程可以访问被修饰的代码。

面试官: 很好。那么,你能举个例子来说明 “synchronized” 关键字的使用方法吗?

候选人: 当然。你可以使用 “synchronized” 关键字来修饰方法或代码块。例如,你可以这样使用:

public synchronized void doSomething() {
    // ...
}

在上面的代码中,“synchronized” 关键字修饰了 “doSomething()” 方法。这意味着在同一时间只有一个线程可以访问该方法。

面试官: 很好。那么,如果我想修饰一个代码块,应该怎么做呢?

候选人: 你可以这样使用 “synchronized” 关键字来修饰一个代码块:

public void doSomething() {
    synchronized (this) {
        // ...
    }
}

在上面的代码中,“synchronized” 关键字修饰了一个代码块,该代码块使用 “this” 作为锁对象。这意味着在同一时间只有一个线程可以访问该代码块。

面试官: 很好。那么,你能解释一下 “synchronized” 关键字的实现原理吗?

候选人: 当一个线程访问一个被 “synchronized” 关键字修饰的方法或代码块时,它会尝试获取该对象的监视器锁。如果该锁已经被其他线程持有,则该线程将被阻塞,直到该锁被释放。下面是一个使用 “synchronized” 关键字的示例:

public class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized void decrement() {
        count--;
    }

    public synchronized int getCount() {
        return count;
    }
}

在上面的代码中,“increment()”、“decrement()” 和 “getCount()” 方法都被 “synchronized” 关键字修饰。这意味着在同一时间只有一个线程可以访问这些方法。

面试官: 很好,你对 “synchronized” 关键字的理解很清晰。那么,你能告诉我 “synchronized” 关键字的缺点吗?

候选人: 当然。使用 “synchronized” 关键字会带来一些性能上的开销,因为每个线程都需要获取锁才能访问被修饰的代码。此外,如果使用不当,还可能会导致死锁等问题。

面试官: 那么,你能告诉我如何避免 “synchronized” 关键字带来的性能开销吗?

候选人: 当然。一种方法是使用 “volatile” 关键字来修饰变量,这可以确保变量的可见性,而不需要使用锁。另一种方法是使用 “java.util.concurrent” 包中的并发集合类,例如 ConcurrentHashMap、CopyOnWriteArrayList 等,这些类使用了更高效的同步机制,可以避免 “synchronized” 关键字带来的性能开销。

面试官: 很好,你的回答很不错。那么,你能告诉我 “synchronized” 关键字和 “Lock” 接口之间的区别吗?

候选人: 当然。 “synchronized” 关键字是 Java 中内置的同步机制,它可以用来修饰方法或代码块,使用起来比较简单,但是它的性能开销比较大。而 “Lock” 接口是 Java 中提供的一种更加灵活的同步机制,它可以实现更细粒度的锁控制,例如可重入锁、读写锁等,使用起来比较复杂,但是它的性能开销比较小。

面试官: 很好,你的回答很清晰。那么,你有没有使用过 “Lock” 接口呢?

候选人: 是的,我有使用过 “Lock” 接口。例如,我曾经使用过 ReentrantLock 类来实现可重入锁,这可以避免 “synchronized” 关键字的性能开销,并且可以实现更细粒度的锁控制。

面试官: 很好,你的经验很丰富。那么,你能告诉我 “Lock” 接口的一些特点吗?

候选人: 当然。 “Lock” 接口的一些特点包括:

  1. 可以实现更细粒度的锁控制,例如可重入锁、读写锁等。
  2. 可以实现公平锁和非公平锁。
  3. 可以实现超时锁和可中断锁。
  4. 可以实现多个条件变量,可以更加灵活地控制线程的等待和唤醒。

面试官: 那么,你能告诉我 “synchronized” 关键字和 “volatile” 关键字之间的区别吗?

候选人: 当然。 “synchronized” 关键字和 “volatile” 关键字都可以用来实现多线程之间的同步,但是它们的作用不同。 “synchronized” 关键字可以确保在同一时间只有一个线程可以访问被修饰的代码,而 “volatile” 关键字可以确保变量的可见性,即当一个线程修改了变量的值后,其他线程可以立即看到这个修改。

面试官: 看来你使用的很好,下面问一点深入的东西。回答不上来也没关系,可以自己想想。

面试官: 好的,那么你能够从 “synchronized” 的底层 Java 实现角度,解释一下它的实现原理吗?

候选人: 当一个线程访问一个被 “synchronized” 关键字修饰的方法或代码块时,它会尝试获取该对象的监视器锁。如果该锁已经被其他线程持有,则该线程将被阻塞,直到该锁被释放。在 Java 中,每个对象都有一个监视器锁,也称为内部锁或互斥锁。当一个线程获取了一个对象的监视器锁后,其他线程就无法访问该对象的被 “synchronized” 关键字修饰的方法或代码块,直到该锁被释放。

在 Java 中,“synchronized” 关键字的实现是基于对象头中的标记字。当一个对象被锁定时,它的标记字会被设置为锁定状态,当锁被释放时,标记字会被清除。在 Java 6 及之前的版本中,对象头中的标记字是 32 位的,其中 25 位用于存储对象的哈希码,4 位用于存储对象的分代年龄,2 位用于存储锁标志位,1 位用于存储是否是偏向锁。在 Java 7 及之后的版本中,对象头中的标记字被重新设计,其中 32 位用于存储对象的哈希码和分代年龄,而锁标志位则被存储在一个单独的数据结构中。

面试官: 很好,你的回答很详细。那么,你能够给我讲讲 “synchronized” 关键字的优化策略吗?

候选人: 当然。在 Java 中,“synchronized” 关键字的性能开销比较大,因为每个线程都需要获取锁才能访问被修饰的代码。为了优化 “synchronized” 关键字的性能,Java 6 及之后的版本中引入了偏向锁、轻量级锁和重量级锁等优化策略。

偏向锁是一种针对单线程访问同步块的优化策略。当一个线程访问一个被 “synchronized” 关键字修饰的代码块时,它会尝试获取该对象的偏向锁。如果该锁没有被其他线程持有,则该线程可以直接获取该锁,而无需进行同步操作。如果该锁已经被其他线程持有,则该线程会尝试升级为轻量级锁或重量级锁。

轻量级锁是一种针对多线程访问同步块的优化策略。当一个线程访问一个被 “synchronized” 关键字修饰的代码块时,它会尝试获取该对象的轻量级锁。如果该锁没有被其他线程持有,则该线程可以直接获取该锁,而无需进行同步操作。如果该锁已经被其他线程持有,则该线程会尝试自旋等待该锁的释放。

重量级锁是一种针对多线程访问同步块的默认策略。当一个线程访问一个被 “synchronized” 关键字修饰的代码块时,它会尝试获取该对象的重量级锁。如果该锁没有被其他线程持有,则该线程可以直接获取该锁,而无需进行同步操作。如果该锁已经被其他线程持有,则该线程会被阻塞,直到该锁被释放。

面试官: 很好,你的回答很详细。那么,你能够给我讲讲 “synchronized” 关键字的底层 Java 源码实现吗?

候选人: 当然。在 Java 中,“synchronized” 关键字的底层实现是通过 monitorenter 和 monitorexit 指令来实现的。当一个线程访问一个被 “synchronized” 关键字修饰的方法或代码块时,它会尝试获取该对象的监视器锁,这可以通过 monitorenter 指令来实现。当该线程执行完被 “synchronized” 关键字修饰的方法或代码块后,它会释放该对象的监视器锁,这可以通过 monitorexit 指令来实现。


参考底层指令:

以下是 JVM 中与 “synchronized” 相关的源码:

  1. monitorenter 指令的实现:
void Interpreter::monitorenter() {
  oop obj = stack_top().get_obj(); // 获取栈顶元素,即被锁定的对象
  if (obj == NULL) { // 如果对象为空,则抛出 NullPointerException 异常
    THROW(vmSymbols::java_lang_NullPointerException());
  }
  BasicLock* lock = obj->mark()->lock(); // 获取对象的锁
  if (lock->displaced_header() == NULL) { // 如果锁没有被其他线程持有,则尝试获取锁
    // Fast path: lock is unheld, try to acquire it
    if (lock->displaced_header() == NULL &&
        lock->displaced_owner() == NULL &&
        lock->set_displaced_header()) {
      // Lock acquired
      return; // 获取锁成功,直接返回
    }
  }
  // Slow path: lock is held or contention detected
  InterpreterRuntime::monitorenter(THREAD, obj); // 获取锁失败,调用 InterpreterRuntime::monitorenter() 方法进行同步操作
}

在上面的代码中,monitorenter 指令的实现是通过获取对象的锁来实现的。如果该锁没有被其他线程持有,则该线程可以直接获取该锁,而无需进行同步操作。如果该锁已经被其他线程持有,则该线程会尝试升级为轻量级锁或重量级锁。

  1. monitorexit 指令的实现:
void Interpreter::monitorexit() {
  oop obj = stack_top().get_obj(); // 获取栈顶元素,即被锁定的对象
  if (obj == NULL) { // 如果对象为空,则抛出 NullPointerException 异常
    THROW(vmSymbols::java_lang_NullPointerException());
  }
  BasicLock* lock = obj->mark()->lock(); // 获取对象的锁
  if (lock->displaced_header() == THREAD) { // 如果锁被当前线程持有,则直接释放锁
    // Fast path: lock is held by this thread, release it
    lock->clear_displaced_header();
    return; // 释放锁成功,直接返回
  }
  // Slow path: lock is held by another thread or unheld
  InterpreterRuntime::monitorexit(THREAD, obj); // 释放锁失败,调用 InterpreterRuntime::monitorexit() 方法进行同步操作
}

在上面的代码中,monitorexit 指令的实现是通过释放对象的锁来实现的。如果该锁被当前线程持有,则该线程可以直接释放该锁,而无需进行同步操作。如果该锁被其他线程持有,则该线程会被阻塞,直到该锁被释放。

  1. ObjectMonitor 类的实现:
class ObjectMonitor : public CHeapObj<mtSynchronizer> {
  friend class VMStructs;
 private:
  volatile intptr_t _header; // 对象头,用于存储锁状态和其他信息
  volatile intptr_t _count; // 计数器,用于记录重入次数
  volatile intptr_t _waiters; // 等待队列长度,用于记录等待锁的线程数
  volatile intptr_t _recursions; // 递归深度,用于记录当前线程已经获取锁的次数
  volatile intptr_t _object; // 对象指针,指向被锁定的对象
  volatile intptr_t _owner; // 持有者指针,指向当前持有锁的线程
  volatile intptr_t _WaitSet; // 等待队列头指针,指向等待队列的头节点
  volatile intptr_t _EntryList; // 等待队列尾指针,指向等待队列的尾节点
  volatile intptr_t _cxq; // 等待队列的条件变量,用于支持条件变量的等待和唤醒操作
  volatile intptr_t _FreeNext; // 空闲链表指针,用于回收 ObjectMonitor 对象
  volatile intptr_t _Responsible; // 责任线程指针,用于记录最后一个释放锁的线程
  volatile intptr_t _SpinFreq; // 自旋频率,用于控制自旋等待的时间
  volatile intptr_t _SpinClock; // 自旋时钟,用于记录自旋等待的时间
  volatile intptr_t _SpinDuration; // 自旋持续时间,用于控制自旋等待的时间
  volatile intptr_t _SpinEarly; // 自旋提前量,用于控制自旋等待的时间
  volatile intptr_t _contentions; // 竞争次数,用于记录获取锁的竞争次数
  volatile intptr_t _succ; // 成功次数,用于记录获取锁的成功次数
  volatile intptr_t _cxqWaitTime; // 条件变量等待时间,用于记录条件变量等待的时间
  volatile intptr_t _reserved; // 保留字段,用于未来扩展
  static int _header_offset; // 对象头偏移量,用于访问对象头中的信息
  static int _count_offset; // 计数器偏移量,用于访问计数器中的信息
  static int _waiters_offset; // 等待队列长度偏移量,用于访问等待队列长度中的信息
  static int _recursions_offset; // 递归深度偏移量,用于访问递归深度中的信息
  static int _object_offset; // 对象指针偏移量,用于访问对象指针中的信息
  static int _owner_offset; // 持有者指针偏移量,用于访问持有者指针中的信息
  static int _WaitSet_offset; // 等待队列头指针偏移量,用于访问等待队列头指针中的信息
  static int _EntryList_offset; // 等待队列尾指针偏移量,用于访问等待队列尾指针中的信息
  static int _cxq_offset; // 条件变量偏移量,用于访问条件变量中的信息
  static int _FreeNext_offset; // 空闲链表指针偏移量,用于访问空闲链表指针中的信息
  static int _Responsible_offset; // 责任线程指针偏移量,用于访问责任线程指针中的信息
  static int _SpinFreq_offset; // 自旋频率偏移量,用于访问自旋频率中的信息
  static int _SpinClock_offset; // 自旋时钟偏移量,用于访问自旋时钟中的信息
  static int _SpinDuration_offset; // 自旋持续时间偏移量,用于访问自旋持续时间中的信息
  static int _SpinEarly_offset; // 自旋提前量偏移量,用于访问自旋提前量中的信息
  static int _contentions_offset; // 竞争次数偏移量,用于访问竞争次数中的信息
  static int _succ_offset; // 成功次数偏移量,用于访问成功次数中的信息
  static int _cxqWaitTime_offset; // 条件变量等待时间偏移量,用于访问条件变量等待时间中的信息
  static int _reserved_offset; // 保留字段偏移量,用于访问保留字段中的信息
  ...
};

在上面的代码中,ObjectMonitor 类是 JVM 中与 “synchronized” 相关的核心类之一。它包含了对象的监视器锁的状态信息,例如锁的持有者、等待队列、递归深度等。在 Java 中,每个对象都有一个 ObjectMonitor 对象与之对应,用于实现 “synchronized” 关键字的同步机制。

面试官: 很好,你的回答很全面,你已进入候补名单。有消息会通知你。

候选人: 源码都背下来了,你给我看这。

最近我在更新《面试1v1》系列文章,主要以场景化的方式,讲解我们在面试中遇到的问题,致力于让每一位工程师拿到自己心仪的offer,感兴趣可以关注JavaPub追更!

🎁目录合集:

Gitee:https://gitee.com/rodert/JavaPub

GitHub:https://github.com/Rodert/JavaPub

http://javapub.net.cn

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

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

相关文章

chatgpt赋能Python-python3反转字符串

Python3反转字符串技巧&#xff1a;让你的代码更高效&#xff01; 你是否曾经在编程时需要对字符串进行反转&#xff0c;但却不知从何入手&#xff1f;Python3提供了简单易用的方法&#xff0c;帮助你更快地反转字符串。本文将介绍Python3中字符串反转的方法以及如何在代码中利…

chatgpt赋能Python-python3如何安装numpy

如何安装numpy&#xff1f; 介绍 在Python编程中&#xff0c;NumPy是使用最广泛的库之一。NumPy是数学和科学计算中的核心模块&#xff0c;主要用于处理数字数据&#xff0c;包括数组计算、线性代数、傅里叶变换、随机数生成等任务。在Python编程中&#xff0c;使用NumPy可以…

chatgpt赋能Python-python3小游戏

Python3小游戏&#xff1a;为你的休闲时光增添乐趣 如果你正处于寻找一款简单好玩的小游戏&#xff0c;那么Python3小游戏将是你的不二之选。作为一名有10年Python编程经验的工程师&#xff0c;我可以说Python3小游戏是一款非常有趣、挑战性适中、易于上手的游戏。接下来&…

工作流 jbpm(图文并茂)

文章目录 1 工作流概述2 jBPM概述3 jBPM开发环境搭建及其配置3.1 准备工作3.2 搭建jBPM开发环境3.3 加入jar包 4 系统数据库表介绍4.1 建表4.2 数据库逻辑关系4.2.1 资源库与运行时的表4.2.2 历史数据表 4.3 表结构4.4 流程操作与数表交互说明 5 相关概念6 流程图说明6.0 快速上…

【学习笔记】Rider调试unity【 联调、断点调试等】(决定弃用vscode了)

目录 一 弃用vscode原委二 Rider调试Unity2.1 启动调试2.2 pausepoint 暂停点2.2.1 使用pausepoint2.2.2 pausepoint&#xfeff;与breakpoint的区别 2.3 不同run configuration区别 三 Rider编辑3.1 补充 四 总结 转载请注明出处&#xff1a;&#x1f517;https://blog.csdn.n…

头歌计算机组成原理实验—运算器设计(4)第4关:16位快速加法器设计

第4关&#xff1a;16位快速加法器设计 实验目的 帮助学生理解成组进位产生函数&#xff0c;成组进位传递函数的概念&#xff0c;熟悉 Logisim 平台子电路的概念&#xff0c;能利用前述实验封装好的4位先行进位子电路以及4位快速加法器子电路构建16位、32位、64位快速加法器&a…

Android 12.0 原生SystemUI下拉通知栏UI背景设置为圆角背景的定制(一)

1.前言 在12.0的系统rom定制化开发中,在原生系统SystemUI下拉状态栏的通知栏的背景是白色四角的背景,由于在产品设计中,需要把四角背景默认改成圆角背景,所以就需要分析系统原生下拉通知栏的每条通知的默认背景,然后通过systemui的通知流程,设置默认下拉状态栏UI中的通知…

java+nodejs+vue+python+php家教信息管理系统

任何网友都可以自由地查看、搜索、发布该家教信息平台的信息。该平台是区别于传统的家教中介的服务平台。学生可以免费查看网站上的家教信息&#xff0c;挑选适合自己的家教&#xff1b;教师可以免费查看网站上的需求信息&#xff0c;挑选适合自己的学生&#xff1b;学生可以发…

chatgpt赋能Python-python3安装scrapy

Python3安装Scrapy&#xff1a;提高爬虫效率的关键 如果你正在从事数据科学、数据分析工作&#xff0c;或者需要收集并存储某些特定网站的数据&#xff0c;Scrapy是一个值得尝试的Python3框架。Scrapy是一个开源的Web爬虫框架&#xff0c;可以方便、高效、快速地对数据进行收集…

Rust每日一练(Leetday0005) 罗马数字、公共前缀、三数之和

目录 13. 罗马数字转整数 Roman to Integer &#x1f31f; 14. 最长公共前缀 Longest Common Prefix &#x1f31f; 15. 三数之和 3Sum &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日…

Qt/QML编程学习之心得:颜色编码方案RGB+YUV+HSI等(八)

说到Human-Machine Interaction 人机交互界面或者说GUI,那么第一反映可能是窗口、各种控件、各种图片,但是所有这些都还需要一个更底层的内容,那就颜色。不管是窗口还是控件,颜色是其最基本的一个属性。 牛顿三棱镜色散实验,发现光的色彩奥妙:当一束白光通过三棱镜时,它…

chatgpt赋能Python-python3控制摄像头拍照

Python3 控制摄像头拍照 介绍 Python3 是一种高级编程语言&#xff0c;被广泛应用于机器学习、人工智能、Web 开发等领域。控制摄像头拍照是 Python3 中非常常见的需求&#xff0c;例如监控、照片拍摄等等。 如何控制摄像头拍照 在 Python3 中&#xff0c;我们可以利用 Ope…

【高效科研工具(一):chatGPT(各种高效网站大集合)】

高效科研工具&#xff08;一&#xff09;&#xff1a;chatGPT&#xff08;各种高效网站大集合&#xff09; 目录 0、前言 1、chatGPT介绍 2、chatGPT官网 3、各种衍生网站&#xff08;&#x1f929;&#x1f929;&#x1f929;&#x1f929;重头戏&#xff01;&#xff01;&a…

插入排序、选择排序、冒泡排序小结(45)

小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 和猫妹学Python&#xff0c;一起趣味学编程。 今日主题 插入排序、选择排序、冒泡排序有什么区别&#xff1f; 原理不同 插入排序是将未排序的元素逐个插入到已排序序列中…

王道考研——计算机网络1.1计算机网络概述

文章目录 0.认识计算机网络1. 计算机网络体系结构1.1概念及功能计算机网络的概念计算机网络的功能计算机网络的发展总结 1.2组成与分类计算机网络的组成计算机网络的分类总结 1.3标准化工作及相关组织&#xff08;408大纲已删&#xff09;标准化工作标准化工作的相关组织总结 1…

总结856

学习目标&#xff1a; 月目标&#xff1a;5月&#xff08;张宇强化前10讲&#xff0c;背诵15篇短文&#xff0c;熟词僻义300词基础词&#xff09; 周目标&#xff1a;张宇强化前3讲并完成相应的习题并记录&#xff0c;英语背3篇文章并回诵 每日必复习&#xff08;5分钟&#…

chatgpt赋能Python-python3怎么降到2

Python3如何降级到Python2 Python3是Python语言的最新版本&#xff0c;但是一些旧的项目或者库可能只支持Python2&#xff0c;这就需要将Python3降级到Python2。本文将介绍如何在MacOS和Windows系统上进行Python3到Python2的降级&#xff0c;并提供详细的步骤和注意事项。 Ma…

chatgpt赋能Python-python3自定义排序

Python3自定义排序 在Python3中&#xff0c;可以使用内置的sorted()函数或list.sort()方法对列表进行排序。这些方法通常基于默认排序规则&#xff0c;即按照元素的字符串表示进行排序。不过&#xff0c;有时候我们需要按照自己定义的规则对列表进行排序。这就需要使用自定义排…

Windows免安装MySQL8.0

Windows免安装MySQL8.0 Windows 上的 MySQL 分为安装版和免安装版。 本文介绍如何在 Windows 上配置免安装版的 MySQL 8.0。 1、下载并安装 VC_redist.x64.exe Windows 版的 MySQL 8.0 依赖 Microsoft Visual C 2019 Redistributable Package&#xff08;VC_redist.x64.exe…

【c++ • STL】初步认识什么是 STL 标准库

&#x1f680; 个人简介&#xff1a;CSDN「博客新星」TOP 10 &#xff0c; C/C 领域新星创作者&#x1f49f; 作 者&#xff1a;锡兰_CC ❣️&#x1f4dd; 专 栏&#xff1a;从零开始的 c 之旅&#x1f308; 若有帮助&#xff0c;还请关注➕点赞➕收藏&#xff0c;不行的…