synchronized底层原理是什么

synchronized底层原理是什么,第1张

虚拟机执行到monitorenter指令的时候,会请求获取对象的monitor锁,基于monitor锁又衍生出一个锁计数器的概念。

当执行monitorenter时,若对象未被锁定时,或者当前线程已经拥有了此对象的monitor锁,则锁计数器+1,该线程获取该对象锁。

当执行monitorexit时,锁计数器-1,当计数器为0时,此对象锁就被释放了。那么其他阻塞的线程则可以请求获取该monitor锁。

扩展资料

synchronized的特性

1、原子性

所谓原子性就是指一个 *** 作或者多个 *** 作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

2、可见性

可见性是指多个线程访问一个资源时,该资源的状态、值信息等对于其他线程都是可见的。

3、有序性

有序性值程序执行的顺序按照代码先后执行。

4、可重入性

synchronized和ReentrantLock都是可重入锁。当一个线程试图 *** 作一个由其他线程持有的对象锁的临界资源时,将会处于阻塞状态,但当一个线程再次请求自己持有对象锁的临界资源时,这种情况属于重入锁。

VMware vSphere vMotion功能是当今虚拟基础架构中最重要的功能之一。自2002年成立以及2003年发布以来,它允许我们将虚拟机的活动状态从一个物理ESXi主机迁移到另一个主机。如今,无缝迁移虚拟机的能力几乎是每个虚拟化部署的重要组成部分。工作负载的可移植性是真正的混合云体验的基础,能够使用 VMware混合云扩展(HCX) 在内部云和公共云之间移动它们。vSphere vMotion仍然是IT行业中最重要的游戏改变者之一。

多年来,vMotion内部开发了很多技术来支持新技术。

此博本文章将重点介绍标准vMotion,这是将活动计算状态从源迁移到目标ESXi主机的标准vMotion。我们还可以执行Storage vMotion,当与标准vMotion结合使用时,它被认为是增强vMotion。其他类型的是长距离vMotion和跨集群vMotion,它们主要是vMotion进程的ESXi端之上的vCenter Server *** 作。

vMotion流程

启动虚拟机迁移后,vCenter Server实例将执行所谓的长时间运行的迁移任务以处理迁移。第一步是执行兼容性检查。是否可以在目标主机上运行虚拟机?考虑可能阻碍实时迁移的可能约束。接下来是告诉源和目标ESXi主机发生了什么。创建包含以下信息的迁移任务:

是否是正在迁移的虚拟机

是否在配置该虚拟机(虚拟硬件,VM选项等)

源ESXi主机是否符合要求

目标ESXi主机是否符合要求

vMotion网络相关问题

vCenter Server实例与源ESXi主机和目标ESXi主机共享迁移规范,确保交换所有必需信息以启动迁移过程。vCenter Server使用Virtual Provisioning X Daemon(VPXD)与ESXi主机进行通信,后者调用ESXi主机上运行的Virtual Provisioning X Agent(VPXA)。VPXA侦听来自VPXD的消息,它接收迁移规范并通过hostd将其传递给VMX进程。主机守护程序(hostd)维护特定于主机的信息和管理访问,包括VMstate等虚拟机遥测。启动迁移时,hostd会将虚拟机置于中间状态,以便在迁移期间无法更改其配置的虚拟机。

虚拟机监视器(VMM)进程负责管理虚拟机内存并将虚拟机存储和网IO请求传输到VMkernel。所有其他对性能无关的IO请求都由VMM转发到VMX。虚拟机扩展(VMX)进程在VMkernel中运行,负责处理对性能不重要的设备的IO。 请注意,VMM仅在迁移期间在源ESXi主机上使用,因为这是虚拟机的活动内存所在的位置。

完成此 *** 作后,源ESXi上的VMkernel迁移模块将打开启用vMotion的网络上的套接字,以设置与目标ESXi主机的通信。

准备阶段到预复制阶段

到目前为止,所有流程和通信路径都已准备好飞机票行实时迁移。准备阶段的目的是确保目标ESXi主机为要迁移的虚拟机预先分配计算资源。此外,虚拟机已经在目标主机上被创建了,但它处于被屏蔽状态。

完成准备阶段后,该过程将进入预复制阶段,在此阶段将内存从源传输到目标ESXi主机。需要跟踪源ESXi主机上的所有虚拟机内存页面。通过这样,vMotion进程知道源虚拟机的哪些内存页面在迁移期间被覆盖或修改(称为脏页面),因为它需要将这些内存页面重新发送到目标主机。

页面追踪

在预复制阶段,虚拟机正在使用的vCPU会被短暂停顿,以安装页面跟踪器。VMkernel迁移模块现在要求VMM启动页面跟踪,因为VMM拥有虚拟机的内存页表状态。下图显示了guest虚拟机 *** 作系统在vMotion期间将数据写入内存时发生的情况:

迭代内存预复制

页面跟踪是一个连续的循环。它将通过使用多次迭代来实现内存预复制收敛。第一次迭代(预拷贝阶段-1)复制虚拟机内存。以下迭代(预拷贝阶段0到 n )用于复制脏内存页面。举个例子,这就是我们实时迁移具有24GB内存的虚拟机时迭代的样子:

阶段-1:复制24GB的虚拟机内存和跟踪页面。当我们发送内存时,它会带来8GB的污染。

阶段0:重新传输脏污的8GB。在这个过程中,内存污染另外3GB。

阶段1:发送3GB。当转移发生时,虚拟机又会污染1GB。

阶段2:发送剩余的1GB。

当内存页面从源复制到目标ESXi主机时,我们需要确定何时能够完成预复制,所以VMM会在每次迭代复制后询问VMkernel是否已完成预复制。当只有将所有内存更改(脏页)复制到目标主机时,才可以执行后续 *** 作。迭代内存预拷贝算法的一部分是将所有目标内存页面与其源匹配。从第0页开始一直到最大或最后一个内存页码,依次检查所有内存页以查看目标页是否与源页同步。

要确定我们是否可以终止预复制,我们需要验证是否可以在<500ms的窗口中完成最后一次内存页面复制。我们可以使用迁移开销中的信息来计算:

迁移传输速率; 以什么速度(GbE)我们在主机之间复制内存数据?

脏页率(GB / s); 客户 *** 作系统覆盖了多少内存页面?

我们还有多少页要传输到目标主机?

如果不是,则发生下一次迭代。如果结果为是,则VMkernel迁移模块将终止预复制过程。

现在,如果脏页率高于迁移传输速率会发生什么?如果是这种情况,那么进行另一次迭代是没有意义的,因为我们永远无法实现内存预复制的收敛,并且迁移将停止。这就是我们在vSphere 50中引入Stun页面发送(SDPS)的原因。基本上,SDPS是VMkernel告诉VMM不运行预定指令但是引入非常短的“睡眠”的一种方式。这可能听起来像是对工作负载性能的影响,但这种情况发生在细粒度级别。正是由于这些非常小的微秒级别的时间窗口,我们可以将vMotion预复制收敛,并完成vMotion工作。

如果脏页面速率>传输速率,则每次迭代执行SDPS。后续迭代仅复制在上一次迭代期间修改的脏内存页。迭代的持续时间越短,客户OS就越不能修改或弄脏其存储页面,从而缩短了下一次预复制迭代。虽然产生一些性能开销,但SDPS通常不会对工作负载造成影响。这些开销对客户 *** 作系统来说是可以忽略不计的。

切换

由VMM终止内存预复制后,所有内存页都驻留在目标ESXi主机上。VMM现在向VMX发送远程过程调用(RPC),它可以挂起源虚拟机。VMX将进入检查点阶段,暂停虚拟机并将检查点数据发送到目标ESXi主机。

在此过程中,目标ESXi主机上的虚拟机将被解除屏蔽,并使用源虚拟机的检查点数据恢复状态。基本流程是:启动目标虚拟机、中断启动过程、再把状态指向迁移过来的源虚拟机内存页,完成启动。所有这些通常发生在100-200ms,这是虚拟机处于不可访问的一个时间,这取决于主机硬件性能、动态的访问负载等各种因素。

到此,虚拟机的vMotion完成

开发十年经验总结,阿里架构师的手写Spring boot原理实践文档

阿里架构师的这份:Redis核心原理与应用实践,带你手撕Redis

Tomcat结构原理详解

说到进程,恐怕面试中最常见的问题就是线程和进程的关系了,那么先说一下答案: 在 Linux 系统中,进程和线程几乎没有区别

Linux 中的进程其实就是一个数据结构,顺带可以理解文件描述符、重定向、管道命令的底层工作原理,最后我们从 *** 作系统的角度看看为什么说线程和进程基本没有区别。

首先,抽象地来说,我们的计算机就是这个东西:

这个大的矩形表示计算机的 内存空间 ,其中的小矩形代表 进程 ,左下角的圆形表示 磁盘 ,右下角的图形表示一些 输入输出设备 ,比如鼠标键盘显示器等等。另外,注意到内存空间被划分为了两块,上半部分表示 用户空间 ,下半部分表示 内核空间

用户空间装着用户进程需要使用的资源,比如你在程序代码里开一个数组,这个数组肯定存在用户空间;内核空间存放内核进程需要加载的系统资源,这一些资源一般是不允许用户访问的。但是注意有的用户进程会共享一些内核空间的资源,比如一些动态链接库等等。

我们用 C 语言写一个 hello 程序,编译后得到一个可执行文件,在命令行运行就可以打印出一句 hello world,然后程序退出。在 *** 作系统层面,就是新建了一个进程,这个进程将我们编译出来的可执行文件读入内存空间,然后执行,最后退出。

你编译好的那个可执行程序只是一个文件,不是进程,可执行文件必须要载入内存,包装成一个进程才能真正跑起来。进程是要依靠 *** 作系统创建的,每个进程都有它的固有属性,比如进程号(PID)、进程状态、打开的文件等等,进程创建好之后,读入你的程序,你的程序才被系统执行。

那么, *** 作系统是如何创建进程的呢? 对于 *** 作系统,进程就是一个数据结构 ,我们直接来看 Linux 的源码:

task_struct 就是 Linux 内核对于一个进程的描述,也可以称为「进程描述符」。源码比较复杂,我这里就截取了一小部分比较常见的。

我们主要聊聊 mm 指针和 files 指针。 mm 指向的是进程的虚拟内存,也就是载入资源和可执行文件的地方; files 指针指向一个数组,这个数组里装着所有该进程打开的文件的指针。

先说 files ,它是一个文件指针数组。一般来说,一个进程会从 files[0] 读取输入,将输出写入 files[1] ,将错误信息写入 files[2] 。

举个例子,以我们的角度 C 语言的 printf 函数是向命令行打印字符,但是从进程的角度来看,就是向 files[1] 写入数据;同理, scanf 函数就是进程试图从 files[0] 这个文件中读取数据。

每个进程被创建时, files 的前三位被填入默认值,分别指向标准输入流、标准输出流、标准错误流。我们常说的「文件描述符」就是指这个文件指针数组的索引 ,所以程序的文件描述符默认情况下 0 是输入,1 是输出,2 是错误。

我们可以重新画一幅图:

对于一般的计算机,输入流是键盘,输出流是显示器,错误流也是显示器,所以现在这个进程和内核连了三根线。因为硬件都是由内核管理的,我们的进程需要通过「系统调用」让内核进程访问硬件资源。

PS:不要忘了,Linux 中一切都被抽象成文件,设备也是文件,可以进行读和写。

如果我们写的程序需要其他资源,比如打开一个文件进行读写,这也很简单,进行系统调用,让内核把文件打开,这个文件就会被放到 files 的第 4 个位置,对应文件描述符 3:

明白了这个原理, 输入重定向 就很好理解了,程序想读取数据的时候就会去 files[0] 读取,所以我们只要把 files[0] 指向一个文件,那么程序就会从这个文件中读取数据,而不是从键盘:

同理, 输出重定向 就是把 files[1] 指向一个文件,那么程序的输出就不会写入到显示器,而是写入到这个文件中:

错误重定向也是一样的,就不再赘述。

管道符其实也是异曲同工,把一个进程的输出流和另一个进程的输入流接起一条「管道」,数据就在其中传递,不得不说这种设计思想真的很巧妙:

到这里,你可能也看出「Linux 中一切皆文件」设计思路的高明了,不管是设备、另一个进程、socket 套接字还是真正的文件,全部都可以读写,统一装进一个简单的 files 数组,进程通过简单的文件描述符访问相应资源,具体细节交于 *** 作系统,有效解耦,优美高效。

首先要明确的是,多进程和多线程都是并发,都可以提高处理器的利用效率,所以现在的关键是,多线程和多进程有啥区别。

为什么说 Linux 中线程和进程基本没有区别呢,因为从 Linux 内核的角度来看,并没有把线程和进程区别对待。

我们知道系统调用 fork() 可以新建一个子进程,函数 pthread() 可以新建一个线程。 但无论线程还是进程,都是用 task_struct 结构表示的,唯一的区别就是共享的数据区域不同 。

换句话说,线程看起来跟进程没有区别,只是线程的某些数据区域和其父进程是共享的,而子进程是拷贝副本,而不是共享。就比如说, mm 结构和 files 结构在线程中都是共享的,我画两张图你就明白了:

所以说,我们的多线程程序要利用锁机制,避免多个线程同时往同一区域写入数据,否则可能造成数据错乱。

那么你可能问, 既然进程和线程差不多,而且多进程数据不共享,即不存在数据错乱的问题,为什么多线程的使用比多进程普遍得多呢 ?

因为现实中数据共享的并发更普遍呀,比如十个人同时从一个账户取十元,我们希望的是这个共享账户的余额正确减少一百元,而不是希望每人获得一个账户的拷贝,每个拷贝账户减少十元。

当然,必须要说明的是, 只有 Linux 系统将线程看做共享数据的进程 ,不对其做特殊看待 ,其他的很多 *** 作系统是对线程和进程区别对待的,线程有其特有的数据结构,我个人认为不如 Linux 的这种设计简洁,增加了系统的复杂度。

在 Linux 中新建线程和进程的效率都是很高的,对于新建进程时内存区域拷贝的问题,Linux 采用了 copy-on-write 的策略优化,也就是并不真正复制父进程的内存空间,而是等到需要写 *** 作时才去复制。 所以 Linux 中新建进程和新建线程都是很迅速的

1、StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,

2、只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,

而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。

3、在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全

而StringBuffer则每次都需要判断锁,效率相对更低

函数scanf()是从标准输入流stdin中读内容的通用子程序,可以读入全部固有类型的数据并自动转换成机内形式,是printf()的补函数。

例如:%s表示读字符串而%d表示读整数,格式串的处理顺序为从左到右,格式说明符逐一与变元表中的变元匹配。直到变元不匹配结束

synchronized 底层如何实现?什么是锁的升级,降级。

*** 作系统分为用户态和内核态,应用级别的程序会运行在用户态,不能访问硬件, *** 作系统内核的程序会运行在内核态,可以直接访问硬件。synchronized 是重量级锁,运行在虚拟机上,而虚拟机是应用级别的程序,运行在用户态,需要通过向 *** 作系统内核程序发出申请,得到反馈获得锁,所以称sychronized为重量级锁。而cas的锁直接运行在用户态,所以称为轻量级锁。

CAS 叫自旋锁或者无锁,是轻量级锁,用于替代synchronized。

CAS的ABA问题可以用版本号解决。可是如果一个线程在比较值相同的情况下,在修改值之前另一个线程有可能提前修改当前值,这怎么避免呢?如下图:

在用c++编写的native方法compareAndSwap中,如果多线程的情况下,会有 lock cmpxchg这条指令来保证线程安全。在底层有多个cpu指向同一条语句(CAS)时,多个cpu通过一条总线通向这条语句,当lock时,会掐断这条总线,直到通向这条语句的cpu执行完成,总线又连上,允许其他cpu *** 作这条语句。

Markword的前四个字节中记录了synchronized的锁信息。

轻量级锁也称为自旋锁。除了重量级锁,其他锁都是在用户态完成。

锁升级指的是轻量级锁升级成重量级锁。

为什么自旋锁可以完成多线程的安全,为什么在竞争激烈的情况下要升级成重量级锁,因为自旋锁这些线程一直在while循环,消耗cpu的资源,而重量级锁这些线程排成队列,不消耗cpu资源,这里又可以分为公平锁和非公平锁。

偏向锁是默认启动的,但是有4s的延迟。

启动偏向锁为什么要延迟4秒:因为启动偏向锁效率不一定会提升。如果一开始就知道会有很多线程竞争锁,那么就不必打开偏向锁,从而提高效率。

Synchronized的底层实现:

Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。Synchronized的作用主要有三个:

从语法上讲,Synchronized可以把任何一个非null对象作为"锁",在HotSpot JVM实现中, 锁有个专门的名字:对象监视器(Object Monitor)

Synchronized总共有三种用法:

注意,synchronized 内置锁 是一种对象锁(锁的是对象而非引用变量), 作用粒度是对象 ,可以用来实现对临界资源的同步互斥访问,是 可重入 的。其可重入最大的作用是避免死锁 ,如:

子类同步方法调用了父类同步方法,如没有可重入的特性,则会发生死锁;

monitorenter :每个对象都是一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

(1) 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者;

(2) 如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1;

(3) 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权;

monitorexit:执行monitorexit的线程必须是objectref所对应的monitor的所有者。指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。

monitorexit指令出现了两次,第1次为同步正常退出释放锁;第2次为发生异步退出释放锁;

通过上面两段描述,我们应该能很清楚的看出Synchronized的实现原理, Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出javalangIllegalMonitorStateException的异常的原因。

你学过 *** 作系统?

而事实是这样的

cpu一直在运行(无论你有没有敲键盘或者点击鼠标)

当发生中断的时候cpu去处理中断(敲键盘或者点击鼠标都是中断的一种)

而在windows中是多线程的,事件驱动的,好像不适合你的单片机(我没学过单片机)

希望对你有帮助

建议你去看看

*** 作系统(理论书不是windows的书哦)

以上就是关于synchronized底层原理是什么全部的内容,包括:synchronized底层原理是什么、标准vMotion的底层运行原理(虚拟机的热迁移原理)、「图文结合」Linux 进程、线程、文件描述符的底层原理等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zz/10162295.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-05
下一篇 2023-05-05

发表评论

登录后才能评论

评论列表(0条)

保存