面试必备:synchronized的底层原理?

最近更新的XX必备系列适合直接背答案,不深究,不喜勿喷。

你能说简单说一下synchronize吗?

可别真简单一句话就说完了呀~

参考回答:

synchronize是java中的关键字,可以用来修饰实例方法、静态方法、还有代码块;主要有三种作用:可以确保原子性、可见性、有序性,原子性就是能够保证同一时刻有且只有一个线程在操作共享数据,其他线程必须等该线程处理完数据后才能进行;可见性就是当一个线程在修改共享数据时,其他线程能够看到,保证可见性,volatile关键字也有这个功能;有序性就是,被synchronize锁住后的线程相当于单线程,在单线程环境jvm的重排序是不会改变程序运行结果的,可以防止重排序对多线程的影响。

补充:我们来看一下上边这个回答,其中有好几个部分可以继续延伸,这里指的延伸就是面试官可以再继续问你的问题。

延伸一:java内存模型的三大特性,或者是说一下java内存模型,或者是synchronize跟java内存模型有什么关系吗?

首先补充为何会问到java内存模型,因为Synchronize的三种作用其实就是java内存模型保证的,再就是这个问题可能单独就蹦到考察java内存模型(JMM)上了。

1、什么是java内存模型:java虚拟机规范中定义了java内存模型是用来屏蔽各种硬件和操作系统间内存的差异,来实现java程序在各平台下并发一致性,再就是,java内存模型并不是真实存在的,他只是一种抽象概念,定义了线程和主内存之间的抽象关系,也就是线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存存储了该线程共享变量的副本。

2、java内存模型的三大特性:java内存模型有三大特性,原子性、可见性、有序性。

原子性:要么执行,要么不执行,主要使用互斥锁Synchronize或者lock来保证操作的原子性;
可见性:在变量修改后将新值同步回主内存,主要有两种实现方式,一是volatile,被volatile修饰的变量发生修改后会立即刷新到主内存;二是使用Synchronize或者lock,当一个变量unlock之前会将变量的修改刷新到主内存中;
有序性:在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序不会影响单线程的执行结果,却会影响多线程并发执行的正确性。主要有两种方式确保有序性:volatile 和 Synchronize 关键字,volatile是通过添加内存屏障的方式来禁止指令重排序,也就是重排序是不能把后面的指令放到内存屏障之前执行;Synchronize是保证同一时刻有且只有一个线程执行同步代码,类似于串联顺序执行代码。

延伸二:你了解先行发生原则(happens-before)吗?

为什么会出现先行发生原则:从上边我们也能看到,如果java内存模型中所有的有序性都要靠volatile和Synchronize来实现的话,那么是非常繁琐的,所以j就出现这么一个《先行发生原则》,用来判断数据是否存在竞争、线程是否安全的重要依据。

参考回答:

先行发生原则是java内存模型用来定义两个操作之间的偏序关系。比如说A操作先发生于B操作,那么在B操作发生之前,A操作修改了内存中的共享变量,那么就会被B操作察觉到。

先行发生原则其中包含8种规则,比如:程序员次序规则,volatile变量规则,线程的启动、中止、中断等规则。

如果再问到能简单介绍一下你说的这几个规则吗?一般不会问这么细的,了解即可。

程序员次序规则:在一个线程内,在程序前面的操作先行发生于后面的操作。

volatile变量规则:对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作。

线程启动规则:Thread 对象的 start() 方法调用先行发生于此线程的每一个动作。

线程加入规则:Thread 对象的结束先行发生于 join() 方法返回。

线程中断规则:对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 interrupted() 方法检测到是否有中断发生。

延伸三:volatile的作用,volatile跟Synchronize的区别

补充:如果上边不答到volatile,可能会跳过这个问题,但是多吹点说不定工资高点。

参考回答:

volatile的作用:volatile关键字主要作用是确保可见性跟有序性,当一个共享变量被volatile修饰,如果一个线程修改了这个共享变量,那么其他线程就会立马可知,强制刷新到主内存。

volatile跟Synchronize的区别:

  1. volatile只能作用域变量,Synchronize可作用域变量、方法、类、同步代码块等;
  2. volatile只能保证可见性和有序性,不能保证原子性,Synchronize三者都可以保证。
  3. volatile不会造成线程阻塞,Synchronize可能会造成线程阻塞。
  4. 在性能方面synchronized关键字是防止多个线程同时执行一段代码,会影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized。

延伸四:你能说说你刚刚提到的重排序吗?

你是否想过,重排序为什么会对多线程产生影响?

参考回答:

重排序是编译器和处理器为了优化程序性能而对指令进行重新排序的一种手段。重排序可以保证最终执行的结果是与程序顺序执行的结果一致,并且只会对不存在数据依赖性的指令进行重排序,重排序在单线程下对最终执行结果是没有影响的,但是在多线程下就会存在问题。

举个例子:

int a = 1;
int b = 2;
int c = a*b;

如上,a与c之间存在数据依赖关系,所以c不能排到A的前面,同时b与c之间也存在数据依赖关系,所以,c也不能排到B的前面,但是a与b之间是不存在数据依赖关系的,所以a与b之间是可以进行重排序的,但是无论怎么重排序都是不会影响到c的值。

但是在多线程中就不一样了,如下代码:

class Test{

    /** 我是变量a **/
    int a = 0;

    /** 我是用来标记变量a是否被写入 **/
    boolean flag = false;

    /** 我是写操作 **/
    public void writer(){
        a = 1/** 第1步 **/
        flag = true/** 第2步 **/
    }

    /** 我是读操作 **/
    public void reader(){
        if(flag){           /** 第3步 **/
            int i = a * a;  /** 第4步 **/
             ......
        }
    } 
}

flag是一个变量,用来表示变量a是否已被写入。这里假设有两个线程A和B ,A线程首先执行writer()方法,随后线程B执行reader()方法。线程B在执行操作第4步的时候,能否看到线程A在操作共享变量a的写入呢?

答案是:在多线程的情况下,不一定能看到;

由于操作1和操作2没有数据依赖关系,编译器和处理器可以对这两个操作重排序;同样,操作3和操作4没有数据依赖关系,编译器和处理器也可以对这两个操作重排序。

具体细节可以看一下这篇文章,了解一下重排序对多线程的影响:https://blog.csdn.net/zhushuai1221/article/details/51491578

你能说一下Synchronize底层原理吗?

参考回答:

synchronized的底层原理是跟monitor有关,也就是视图器锁,每个对象都有一个关联的monitor,当Synchronize获得monitor对象的所有权后会进行两个指令:加锁指令monitorenter跟减锁指令monitorexit。

monitor里面有个计数器,初始值是从0开始的。如果一个线程想要获取monitor的所有权,就看看它的计数器是不是0,如果是0的话,那么就说明没人获取锁,那么它就可以获取锁了,然后将计数器+1,也就是执行monitorenter加锁指令;monitorexit减锁指令是跟在程序执行结束和异常里的,如果不是0的话,就会陷入一个堵塞等待的过程,直到为0等待结束。

最后

博客地址:https://www.cgblog.com/niceyoo

如果觉得这篇文章有丶东西,不放关注一下我,关注是对我最大的鼓励~

18年专科毕业后,期间一度迷茫,最近我创建了一个公众号用来记录自己的成长。

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

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

相关文章

hadoop fs 命令详解

转载: https://blog.csdn.net/bgk083/article/details/49454209转载于:https://www.cnblogs.com/water-green/p/9050122.html

数组追加数组,小程序数组里面追加数组如何操作?

由于写错小程序生命周期函数方法名称「onLoad > onload」,一直以为自己用错了push… 需求描述 var arr[];var value [ a,b,c,d ]; 已有数组arr,获取的数据形式为数组value,需要将value追加值arr数组中,即[[],[]] 尝试过程 1、…

[js] 你有使用过pjax吗?它的原理是什么?

[js] 你有使用过pjax吗?它的原理是什么? pushState ajax pjax个人简介 我是歌谣,欢迎和大家一起交流前后端知识。放弃很容易, 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

Python3回文相关算法小结

[本文出自天外归云的博客园] 总结一下关于回文相关的算法: 判断字符串本身是否是回文返回字符串中的所有子串找到字符串中包含的所有回文判断字符串中是否包含回文将字符串变成一个不包含回文的字符串代码如下: # 判断字符串本身是否是回文 def is_huiwe…

面试必备:CAS无锁机制

CAS无锁机制原理,面试高频问题之一,其实,日常开发中并不会直接使用CAS无锁机制,都是通过一系列封装好的工具类来使用, 说不定面试官不提问,都不知道有这么个东西存在。 1、能说一下你对CAS的理解吗&#xf…

[js] 根据元素ID遍历树形结构,查找到所有父元素ID

[js] 根据元素ID遍历树形结构,查找到所有父元素ID var list [{ "orgId": 1, "orgName": "校长办公室1", "parentId": 0 },{ "orgId": 2, "orgName": "校长办公室2", "parentId"…

安装oracle和plsql心得

最近给两台裸机安装oracle,plsql,java运行环境等相关。从中分享一些小心得。 1.oracle安装,基本都是安装ORACLE11g的,中途遇到报错了是因为64位windows系统,需要重新拷贝对应文件夹到对应文件下。 2.plsql安装的话&…

[js] 举例说明Object.defineProperty会在什么情况下造成循环引用导致栈溢出?

[js] 举例说明Object.defineProperty会在什么情况下造成循环引用导致栈溢出? var data {count: 1,value: 2 } Object.defineProperty(data, count, {enumerable: true,configurable: true,get: function () {console.log(你访问了count, this.count); // 循环读取…

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})注解作用

有小伙伴在群里问到对 SpringBootApplication(exclude { DataSourceAutoConfiguration.class}) 有点疑惑,故记之。 exclude,排除此类的AutoConfig,即禁止 SpringBoot 自动注入数据源配置,怎么讲? DataSourceAutoConfi…

[js] 写一个方法,当给定数字位数不足8位时,则在左边补充0以补足8位数的方法

[js] 写一个方法&#xff0c;当给定数字位数不足8位时&#xff0c;则在左边补充0以补足8位数的方法 function padNumber(n, targetLen, placeholder) {const arr ("" n).split("");const diff arr.length - targetLen;if (diff < 0) {return Array(…

小程序json字符串取值问题,怎么取出来的是undefined,eval函数不能用?

1、后端返回的值 "{\"msg\":\"InvalidParameterValue.NoFaceInPhoto-图片中没有人脸。\",\"code\":500}” 关于为何会返回如上json字符串的场景&#xff1a;文件上传「声明了Content-Type」 wx.uploadFile({ url: common.apiServerwx/user…

asp.net mvc 自定义全局过滤器 验证用户是否登录

一般具有用户模块的系统都需要对用户是否登录进行验证&#xff0c;如果用户登录了就可以继续操作&#xff0c;否则退回用户的登录页面 对于这样的需求我们可以通过自定义一个独立的方法来完成验证的操作&#xff0c;但是这样代码的重复率就大大提高了 对于这样的需求&#xff0…

支付宝手机h5网页支付不再提供「继续浏览器付款」按钮了吗

来自圈友的疑惑&#xff0c;记录一下 之前写过一篇「支付宝手机h5支付的文章」&#xff0c;如果下载运行过Demo的小伙伴肯定发现了一个问题 > 「Demo中有显示继续浏览器付款按钮&#xff0c;但自己实际环境并没有」 难道是操作不对&#xff1f; 其实不然&#xff0c;这是两个…

[js] innerHTML与outerHTML有什么区别?

[js] innerHTML与outerHTML有什么区别&#xff1f; <div id"test"><h5>就是喜欢你</h5></div>document.getElementById("test").innerHTML //<h5>就是喜欢你</h5> document.getElementById("test").outHTM…

.NETFramework-Web.Mvc:ViewResult

ylbtech-.NETFramework-Web.Mvc&#xff1a;ViewResult1.程序集 System.Web.Mvc, Version5.2.3.0, Cultureneutral, PublicKeyToken31bf3856ad364e35返回顶部 1、#region 程序集 System.Web.Mvc, Version5.2.3.0, Cultureneutral, PublicKeyToken31bf3856ad364e35 // c:\users\…

ConcurrentHashMap底层原理?

本文为面试必备系列篇&#xff0c;不深入叙述&#xff0c;具体细节可自行查询。 可能会问的问题 1、用过ConcurrentHashMap吗&#xff1f;2、为什么要用ConcurrentHashMap&#xff1f;3、HashMap与HashTable的区别&#xff0c;引出ConcurrentHashMap…4、HashMap在多线程环境下…

[js] js操作节点的方法有哪些?

[js] js操作节点的方法有哪些&#xff1f; 创建节点createElement() 创建一个元素节点createTextNode() 创建一个文本节点createAttribute() 创建一个属性节点createComment() 创建一个注释节点插入节点appendChild() 把节点插入到父节点的末尾insertBefore() 把节点插入到父节…

【刷题】洛谷 P2709 小B的询问

题目描述 小B有一个序列&#xff0c;包含N个1~K之间的整数。他一共有M个询问&#xff0c;每个询问给定一个区间[L..R]&#xff0c;求Sigma(c(i)^2)的值,其中i的值从1到K&#xff0c;其中c(i)表示数字i在[L..R]中的重复次数。小B请你帮助他回答询问。 输入输出格式 输入格式&…

支付宝支付-当面付之扫码支付「扫码支付」

前言 支付宝支付—沙箱环境使用支付宝支付-支付宝PC端扫码支付支付宝支付-手机浏览器H5支付支付宝支付-当面付之扫码支付「本文」 当面付包含两种支付方式&#xff1a;商品条形码支付 扫码支付 经过前面两篇PC端扫码支付、手机H5支付&#xff0c;我们可以看到一个共同的特点就…

[js] 写一个格式化时间的方法

[js] 写一个格式化时间的方法 function dateToString(date, format yyyy-MM-dd) {const d new Date(date);let result format;const _config {y: d.getFullYear(),M: d.getMonth() 1, // 月d: d.getDate(), // 日h: d.getHours(), // 小时m: d.getMinutes(), // 分s: d.g…