面试必备: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,一经查实,立即删除!

相关文章

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

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

.NETFramework-Web.Mvc:ViewResult

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

ConcurrentHashMap底层原理?

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

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

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

MybatisCodeHelperNew-2.8.1-191-201插件使用

本文测试环境IDEA_2020.1,文中提供了MacOS用户操作截图 1、文件解压后放置plugs插件目录「Windows」 扫码回复「139」 下载后将文件解压,将压缩包内的 MyBatisCodeHelper-Pro 放入 IDEA 安装目录的 plugins 目录。 2、如果你是MacOS用户「MacOS」 同样找…

Redis简单案例(四) Session的管理

Redis简单案例(四) Session的管理 原文:Redis简单案例(四) Session的管理负载均衡,这应该是一个永恒的话题,也是一个十分重要的话题。毕竟当网站成长到一定程度,访问量自然也是会跟着增长,这个时候, 一般都会对其进行负…

MacOS中Elasticsearch的安装「借助Homebrew」

1、Homebrew 如果你已经安装过Homebrew了,那么你可以跳过这一步,直接进行Elasticsearch安装步骤; Homebrew是一款MacOS平台下的软件包管理工具,拥有安装、卸载、更新、查看、搜索等很多实用的功能,强烈推荐安装。 请…

负载均衡中使用 Redis 实现共享 Session

最近在研究Web架构方面的知识,包括数据库读写分离,Redis缓存和队列,集群,以及负载均衡(LVS),今天就来先学习下我在负载均衡中遇到的问题,那就是session共享的问题。 一、负载均衡 负…

Typora中使用Gitee图床

1、前言 之前好友写了一篇「使用gitee作为图床 ,写markdown自动上传文件」,初衷是由于我一直使用的是Typora来写博客「力推」,但之前的版本都不支持图床功能,现在新版本已经有了图床功能了,赶紧入坑。 本篇环境:MacOS…

【NOIP2017模拟6.25】小W的动漫

题目 小W最近迷上了日本动漫,每天都有无数部动漫的更新等着他去看,所以他必须将所有的动漫排个顺序,当然,虽然有无数部动漫,但除了1号动漫,每部动漫都有且仅有一部动漫是它的前传(父亲&#xff…

用Elasticsearch代替数据库存储日志方式

之前的项目中一直使用的是数据库表记录用户操作日志的,但随着时间的推移,数据库log单表是越来越大「不考虑删除」,再加上近期项目中需要用到Elasticsearch,所以干脆把这些用户日志迁移到ES上来了。 环境:SpringBoot2.2…

[js] 写一个方法实现promise失败后自动重试

[js] 写一个方法实现promise失败后自动重试 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv&…

如何理解Java中的自动拆箱和自动装箱?

小伟刚毕业时面的第一家公司就被面试官给问住了&#xff0c;记忆尤深啊… 如何理解Java中的自动拆箱和自动装箱&#xff1f; 自动拆箱&#xff1f;自动装箱&#xff1f;什么鬼&#xff0c;听都没听过啊&#xff0c;这…这…知识盲区… 回到家后小伟赶紧查资料&#xff0c;我…

基于Docker的Redis集群简单搭建

环境&#xff1a;Docker ( Redis:5.0.5 * 3 ) 1、拉取镜像 docker pull redis:5.0.52、创建Redis容器 创建三个 redis 容器&#xff1a; redis-node1&#xff1a;6379redis-node2&#xff1a;6380redis-node3&#xff1a;6381 docker create --name redis-node1 -v /data…

Python 全栈开发十 socket网络编程

一、客户端&#xff08;client&#xff09;服务端&#xff08;sever&#xff09;架构 在计算机中有很多常见的C/S架构&#xff0c;例如我们的浏览器是客户端、而百度网站和其他的网站就是服务端&#xff1b;视频软件是客户端&#xff0c;提供视频的腾讯、优酷、爱奇艺就是服务端…

基于Docker方式实现Elasticsearch集群

文本环境&#xff1a;Docker (Elasticsearch6.8.5 * 3) 1、拉取Elasticsearch docker pull elasticsearch6.8.52、创建es挂载目录 创建3个文件夹用于存放es挂载地址&#xff1a;es01、es02、es03 [rootCentOS7 ~]# mkdir /es-cluster [rootCentOS7 ~]# cd /es-cluster/ [ro…

基于Docker搭建Gitlab代码存储

关于Docker搭建Gitlab&#xff0c;在19年时就已经在博客发过文章了&#xff0c;今天重新回顾一下。 1、拉取镜像 docker pull gitlab/gitlab-ce默认拉取最新版本&#xff1a; 2、创建Gitlab配置 创建GitLab 的配置 (etc) 、 日志 (log) 、数据 (data) 放到容器之外&#xff…

读书笔记--Android Gradle权威指南(上)

本篇文章已授权微信公众号 dasu_Android&#xff08;大苏&#xff09;独家发布 最近看了一本书《Android Gradle 权威指南》&#xff0c;对于 Gradle 理解又更深了&#xff0c;但不想过段时间就又忘光了&#xff0c;所以打算写一篇读书笔记&#xff0c;将书中一些我个人觉得蛮有…

基于Docker搭建私有镜像仓库

通常我们在docker中拉取的镜像都是在docker hub在线存储库中获取的&#xff0c;这个在线存储库里的docker镜像可以由任何用户发布和使用&#xff0c;显然这在某些场景下是不适用的&#xff0c;比如某些互金的隐私项目&#xff0c;或者是公司完全处于内网状态不能访问外网&#…

volatile理解了吗?

到这里大家感觉自己对volatile理解了吗&#xff1f; 如果理解了&#xff0c;大家考虑这么一个问题&#xff1a;ReentrantLock&#xff08;或者其它基于AQS实现的锁&#xff09;是如何保证代码段中变量&#xff08;变量主要是指共享变量&#xff0c;存在竞争问题的变量&…