BAT美团滴滴java面试大纲(带答案版)之三:多线程synchronized
继续面试大纲系列文章。
从这一篇开始,我们进入ava编程中的一个重要领域---多线程!多线程就像武学中对的吸星大法,理解透了用好了可以得道成仙,俯瞰芸芸众生;而滥用则会遭其反噬。
在多线程编程中要渡的首个“劫”,则是Synchronized。了解其底层实现,无论是在面试中还是在平时工作中,都大有裨益。我们知其然,知其所以然,才能得心应手少挖坑。
我们知道,多线程的核心思想是通过增加线程数量来并发的运行,来提高效率,也就是数量决胜论,而不是质量决胜(提高每个线程的处理能力)。多线程编程中面临的最大挑战,是如何解决多个线程同时修改一个公用的变量所带来的变量值不确定性问题。顺着这个思路分析,常用办法,无非就是,要么对变量动手,在一个线程修改时,变量值被锁定。要么是对修改的操作动手,在该段代码执行时,对其加锁,其他线程不可以在同一时刻进入该段代码执行。
Synchronized,正是实现了后一种办法。
Synchronized
- 问:你平时涉及到多线程编程多不多?谈谈你对Synchronized锁的理解
- 分析:多从实现原理,工作机制来描述
- 答:
- 在多线程编程中,为了达到线程安全的目的,我们往往通过加锁的方式来实现。而Synchronized正是java提供给我们的非常重要的锁之一。它属于jvm级别加锁,底层实现是:在编译过程中,在指令级别加入一些标识来实现的。例如,同步代码块,会在同步代码块首尾加入monitorenter和monitorexit字节码指令,这两个指令都需要一个reference类型的参数,指明要加锁和解锁的对象,同步方法则是通过在修饰符上加acc_synchronized标识实现。在执行到这些指令(标识)时,本质都是获取、占有、释放monitor锁对象实现互斥,也就是说,同一时刻,只能有一个线程能成功的获取到这个锁对象。我们看一段加了synchronized关键字的代码编译后的字节码。编译前:
1 public class test {
2 public test() {
3 }
4 public static void main(String[] args) {
5 synchronized(new Object()){
6 int i = 0;
7 }
8 }
9 }编译后:
public class test extends java.lang.Object{
public test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."":()V
4: nop
5: return public static void main(java.lang.String[]);
Code:
0: new #2; //class Object
3: dup
4: invokespecial #1; //Method java/lang/Object."":()V
7: dup
8: astore_1
9: monitorenter // Enter the monitor associated with object
10: iconst_0
11: istore_2
12: nop
13: aload_1
14: monitorexit // Exit the monitor associated with object
15: goto 23
18: astore_3
19: aload_1
20: monitorexit // Be sure to exit monitor...
21: aload_3
22: athrow
23: nop
24: return
Exception table:
from to target type
10 15 18 any
18 21 18 any }重点关注14行,和20行。
- 在使用Synchronized时,用到的方法是wait和notify(或notifyAll),他们的原理是,调用wait方法后,线程让出cpu资源,释放锁,进入waiting状态,进入等待队列【第一个队列】。当有其他线程调用了notify或者notifyAll唤醒时,会将等待队列里的线程对象,移入阻塞队列【第二个队列】,状态是blocked,等待锁被释放后(这个释放时机,由虚拟机来决定,人为无法干预),开始竞争锁。
- Synchronized无法中断正在阻塞队列或者等待队列的线程。
- 在多线程编程中,为了达到线程安全的目的,我们往往通过加锁的方式来实现。而Synchronized正是java提供给我们的非常重要的锁之一。它属于jvm级别加锁,底层实现是:在编译过程中,在指令级别加入一些标识来实现的。例如,同步代码块,会在同步代码块首尾加入monitorenter和monitorexit字节码指令,这两个指令都需要一个reference类型的参数,指明要加锁和解锁的对象,同步方法则是通过在修饰符上加acc_synchronized标识实现。在执行到这些指令(标识)时,本质都是获取、占有、释放monitor锁对象实现互斥,也就是说,同一时刻,只能有一个线程能成功的获取到这个锁对象。我们看一段加了synchronized关键字的代码编译后的字节码。编译前:
4.扩展:Synchronized提供了以下几种类型的锁:偏向锁、轻量级锁、重量级锁。在大部分情况下,并不存在多线程竞争,更常见的是一个线程多次获取同一个锁。那么很多的消耗,其实是在锁的获取与释放上。Synchronized一直在被优化,可以说Synchronized虽然推出的较早,但是效率并不比后来推出的Lock差。
- 偏向锁:在jdk1.6中引入,目的是消除在无竞争情况下的同步原语(翻译成人话就是,即使加了synchronized关键字,但是在没有竞争的时候,没必要去做获取-持有-释放锁对象的操作,提高程序运行性能)。怎么做呢?当锁对象第一次被线程A获取时,虚拟机会把对象头中的标志位设置为01,也就是代表偏向模式。同时把代表这个线程A的ID,通过CAS方式,更新到锁对象头的MarkWord中。相同的线程下次再次申请锁的时候,只需要简单的比较线程ID即可。以上操作成功,则成功进入到同步代码块。如果此时有其他线程B来竞争该锁,分两种情况做不同的处理:
- 如果线程A已执行完(并不会主动的修改锁对象的状态),会直接撤销锁对象的状态为未锁定,标志位为00;
- 如果线程A还在持有该锁,则锁升级为轻量级锁。
- 轻量级锁:也是JDK1.6中引入的,轻量级,是相对于使用互斥量的重量级锁来说的。线程发生竞争锁的时候,不会直接进入阻塞状态,而是先尝试做CAS修改操作,进入自旋,这个过程避免了线程状态切换的开销,不过要消耗cpu资源。详细过程是:
- 线程尝试进入同步代码块,如果锁对象未被锁定,在当前线程对应的栈帧中,建立锁记录的空间,用于存储锁对象Mark Word的拷贝。
- 然后JVM用CAS方式尝试将锁对象的MarkWord内容替换为指向前述“锁记录”的指针。如果成功,当前线程则持有了锁,处于轻量级锁定状态;如果失败,会首先检查当前MarkWord是否已经指向当前线程栈帧的“锁记录”,如果是,就说明当前线程已经拥有了这个锁,直接重入即可。否则就表明是其他线程持有锁,那么进入自旋(其实就是重试CAS修改操作)。
- 释放锁时,是使用CAS来讲MarkWord和“锁记录”里的内容互换。如果成功,成功释放;如果事变,表明当前锁存在竞争(被其他线程修改了MarkWord里的数据),此时,锁会升级为重量级锁。
- 重量级锁:也就是我们使用的互斥量方式实现的锁,当存在多线程竞争时,只要没拿到锁,就会进入阻塞状态,主要消耗是在阻塞-唤起-阻塞-唤起的线程状态切换上。
- 上面介绍的三种类型的锁,是JVM来负责管理使用哪种类型锁,以及锁的升级(注意,没有降级)。
- 这里涉及到锁升级,对象头MarkWord等内容,如果详细说,可能又是一大篇文章了,如果大家感兴趣,可以另起一篇。
- synchronized就先介绍到这里,下一篇预告:ReentrantLock相关
- 如果你看的爽,请点击右下角的“推荐”,是对小端坚持分享原创的最大鼓励。也可以关注小端的个人公众号 : pnxsxb ,会分享更多的原创技术文章。
欢迎扫描以下二维码:

BAT美团滴滴java面试大纲(带答案版)之三:多线程synchronized的更多相关文章
- BAT美团滴滴java面试大纲(带答案版)之三:多线程Lock
继续面试大纲系列文章. 这是多线程的第二篇. 多线程就像武学中对的吸星大法,理解透了用好了可以得道成仙,俯瞰芸芸众生:而滥用则会遭其反噬. 在多线程编程中要渡的第二个“劫”,则是Lock.在很多时候, ...
- 金三银四跳槽季,BAT美团滴滴java面试大纲(带答案版)之一:Java基础篇
Java基础篇: 题记:本系列文章,会尽量模拟面试现场对话情景, 用口语而非书面语 ,采用问答形式来展现.另外每一个问题都附上“延伸”,这部分内容是帮助小伙伴们更深的理解一些底层细节的补充,在面试中可 ...
- BAT美团滴滴java面试大纲(带答案版)之四:多线程Lock
每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 这是多线程的第二篇. 多线程就像武学中对的吸星大法,理解透了用好了可以得道成仙,俯瞰芸 ...
- Java面试大纲-java面试该做哪些准备,java开发达到这样的水平可以涨工资
Java培训结束,面临的就是毕业找工作.在找工作时,就要针对性地做充分的面试准备.准备不充分的面试,完全是浪费时间,更是对自己的不负责. 上海尚学堂Java培训整理出Java面试大纲,其中大部分都是面 ...
- 2019滴滴java面试总结 (包含面试题解析)
2019滴滴java面试总结 (包含面试题) 本人6年开发经验.今年年初找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.滴滴等公司offer,岗位是既有php也有Java后端开发,最终选择去了滴滴 ...
- Java面试宝典(2018版)
置顶 2018年11月10日 23:49:18 我要取一个响亮的昵称 阅读数:8893 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/chen ...
- Java面试:投行的15个多线程和并发面试题
多线程和并发问题已成为各种 Java 面试中必不可少的一部分.如果你准备参加投行的 Java 开发岗位面试,比如巴克莱银行(Barclays).花旗银行(Citibank).摩根史坦利投资公司(Mor ...
- Java面试:投行的15个多线程和并发面试题(转)
多线程和并发问题已成为各种 Java 面试中必不可少的一部分.如果你准备参加投行的 Java 开发岗位面试,比如巴克莱银行(Barclays).花旗银行(Citibank).摩根史坦利投资公司(Mor ...
- 2019 滴滴java面试笔试总结 (含面试题解析)
本人5年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.滴滴等公司offer,岗位是Java后端开发,因为发展原因最终选择去了滴滴,入职一年时间了,也成为了面试官, ...
随机推荐
- 跨域调用接口的方法之一:$.ajaxSetup()
跨域查询接口的数据,之前在公司时有发生过,产生的原因是,本地请求的域名或IP地址不一致,解除方法,也是修改域名和IP地址.比如: 接口中的数据来自IP地址:192.168.1.23/get.php 如 ...
- Django Admin后台管理用户密码修改
方法一 在Terminal中执行:python manage.py changepassword your_name(其中“your_name”为你要修改密码的用户名),根据提示内容修改即可. 方法二 ...
- 机器学习之隐马尔科夫模型HMM(六)
摘要 隐马尔可夫模型(Hidden Markov Model,HMM)是统计模型,它用来描述一个含有隐含未知参数的马尔科夫过程.其难点是从可观察的参数中确定该过程的隐含参数,然后利用这些参数来作进一步 ...
- IIS 使用OpenSSL 生成的自签名证书,然后使用SingalR 客户端访问Https 站点通信
使用SignalR 的客户端去发送消息给使用 https 部署的站点,官方文档目前并没有详细的教程,所以在此记录下步骤: 使用管理员身份打开cmd 窗口,选择一个整数保存文件夹的地址,切换到对应的文件 ...
- c/c++线性队列
线性队列 队列是先进先出,和栈相反. 不循环的队列就是浪费空间,如果tail到了最大值后,即使前面出队了,有空的位置,也不能再入队. seqqueue.h #ifndef __SEQQUEUE__ # ...
- iOS 验证码按钮倒计时
在app 注册或者登录 需要验证码的地方.为了避免短时间内刷验证码.往往会加上一层验证. 倒计时结束后.可以重新获取! 代码实现如下: // _CountdownTime 倒计时总时间: //_tim ...
- 修改linux 默认SHELL
首先你得查看可以用的shell: 1.命令:chsh -l ,结果如下: /bin/sh/bin/bash/sbin/nologin/usr/bin/sh/usr/bin/bash/usr/sbin/ ...
- ubuntu16.04如何安装多个版本的CUDA
我的机器是CUDA16.04的,之前装过CUDA10.0,因为一些原因,现在需要安转CUDA9.0. 1.首先https://developer.nvidia.com/cuda-90-download ...
- 【算法】LeetCode算法题-Merge Two Sorted List
这是悦乐书的第148次更新,第150篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第7题(顺位题号是21).合并两个已排序的链表并将其作为新链表返回. 新链表应该通过拼接 ...
- 【项目 · Wonderland】需求规格说明书 · 终版
[项目 · Wonderland]需求规格说明书 · 终版 Part 0 · 简 要 目 录 Part 1 · 流 程 / 分 工 Part 2 · 需 求 规 格 说 明 书 Part 1 · 流 ...