1.synchronized关键字
2.MarkWord和Synchronized的源码r源锁升级机制详解(JDK8)
3.找到卡顿来源,BlockCanary源码精简分析
4.QEMU Monitor 使ç¨
5.synchronize底层原理
6.FreeBSD 7.3 安装GNOME图形界面
synchronized关键字
并发编程中的关键点在于数据同步、线程安全和锁。源码r源编写线程安全的源码r源代码,核心在于管理对共享和可变状态的源码r源访问。
共享意味着变量可以被多个线程访问,源码r源mxnet源码目录结构而可变则意味着变量的源码r源值在其生命周期内可以变化。
当多个线程访问某个状态变量,源码r源且有一个线程执行写入操作时,源码r源必须使用同步机制来协调对这些线程的源码r源访问。
Java中的源码r源主要同步机制是关键字synchronized,它提供了一种独占的源码r源加锁方式。
以下是源码r源关于synchronized关键字的几个方面:
关键字synchronized的特性:
不可中断:synchronized关键字提供了独占的加锁方式,一旦一个线程持有了锁对象,源码r源其他线程将进入阻塞状态或等待状态,源码r源直到前一个线程释放锁,中间过程不可中断。
原子性:synchronized关键字的不可中断性保证了它的原子性。
可见性:synchronized关键字包含了两个JVM指令:monitor enter和monitor exit,它能够保证在任何时候任何线程执行到monitor enter时都必须从主内存中获取数据,而不是从线程工作内存获取数据,在monitor exit之后,工作内存被更新后的值必须存入主内存,从而保证了数据可见性。
有序性:synchronized关键字修改的同步方法是串行执行的,但其所修饰的代码块中的指令顺序还是会发生改变的,这种改变遵守java happens-before规则。
可重入性:如果一个拥有锁持有权的线程再次获取锁,则monitor的计数器会累加1,当线程释放锁的时候也会减1,直到计数器为0表示线程释放了锁的持有权,在计数器不为0之前,其他线程都处于阻塞状态。
关键字synchronized的用法:
synchronized关键字锁的是对象,修饰的可以是代码块和方法,但不能修饰class对象以及变量。
在开发中最常用的是用synchronized关键字修饰对象,可以控制锁的论坛源码对接蓝鸟粒度,所以针对最常用的场景,先来看看它的字节码文件。
TIPS:在使用synchronized关键字时注意事项
锁膨胀:
在jdk1.6之前,线程在获取锁时,如果锁对象已经被其他线程持有,此线程将挂起进入阻塞状态,唤醒阻塞线程的过程涉及到了用户态和内核态的切换,性能损耗比较大。
synchronized作为亲儿子,混的太差肯定不行,在jdk1.6对其进行了优化,将锁状态分为了无锁状态、偏向锁、轻量级锁、重量级锁。
锁的升级过程既是:
在了解锁的升级过程之前,重点理解了monitor和对象头。
每一个对象都与一个monitor相关联,monitor对象与实例对象一同创建并销毁,monitor是C++支持的一个监视器。锁对象的争夺即是争夺monitor的持有权。
在OpenJdk源码中找到了ObjectMonitor的源码:
owner:指向线程的指针。即锁对象关联的monitor中的owner指向了哪个线程表示此线程持有了锁对象。
waitSet:进入阻塞等待的线程队列。当线程调用wait方法之后,就会进入waitset队列,可以等待其他线程唤醒。
entryList:当多个线程进入同步代码块之后,处于阻塞状态的线程就会被放入entryList中。
那什么是对象头呢?它与synchronized又有什么关系呢?
在JVM中,对象在内存中分为3块区域:
我们先通过一张图了解下在锁升级的过程中对象头的变化:
接下来我们分析锁升级的过程:
第一个分支锁标志为:
当线程运行到同步代码块时,首先会判断锁标志位,如果锁标志位为,则继续判断偏向标志。
如果偏向标志为0,则表示锁对象未被其他线程持有,可以获取锁。查询系统源码下载此时当前线程通过CAS的方法修改线程ID,如果修改成功,此时锁升级为偏向锁。
如果偏向标志为1,则表示锁对象已经被占有。
进一步判断线程id是否相等,相等则表示当前线程持有的锁对象,可以重入。
如果线程id不相等,则表示锁被其他线程占有。
需进一步判断持有偏向锁的线程的活动状态,如果原持有偏向锁线程已经不活动或者已经退出同步代码块,则表示原持有偏向锁的线程可以释放偏向锁。释放后偏向锁回到无锁状态,线程再次尝试获取锁。主要是因为偏向锁不会主动释放,只有其他线程竞争偏向锁的时候才会释放。
如果原持有偏向锁的线程没有退出同步代码块,则锁升级为轻量级锁。
偏向锁的流程图如下:
第二个分支锁标志为:
在第一个分支中我们了解到在如果偏向锁已经被其他线程占有,则锁会被升级为轻量级锁。
此时原持有偏向锁的线程的栈帧中分配锁记录Lock Record,将对象头中的Mark Word信息拷贝到锁记录中,Mark Word的指针指向了原持有偏向锁线程中的锁记录,此时原持有偏向锁的线程获取轻量级锁,继续执行同步块代码。
如果线程在运行同步块时发现锁的标志位为,则在当前线程的栈帧中分配锁记录,拷贝对象头中的Mark Word到锁记录中。通过CAS操作将Mark Word中的指针指向自己的锁记录,如果成功,则当前线程获取轻量锁。
如果修改失败,则进入自旋,不断通过CAS的方式修改Mark Word中的指针指向自己的锁记录。
当自旋超过一定次数(默认次),则升级为重量锁。myeclipse源码怎么导入
轻量级流程图如下图:
第三个分支锁标志位为:
锁标志为时,此时锁已经为重量锁,线程会先判断monitor中的owner指针指向是否为自己,是则获取重量锁,不是则会挂起。
整个锁升级过程中的流程图如下,如果看懂了一定要自己画一遍。
总结:
synchronized关键字是一种独占的加锁方式,不可中断,保证了原子性、可见性和有序性。
synchronized关键字可用于修饰方法和代码块,但不能用于修饰变量和类。
多线程在执行同步代码块时获取锁的过程在不同的锁状态下不一样,偏向锁是修改Mark Word中的线程ID,轻量锁是修改Mark Word的指针指向自己的锁记录,重量锁是修改monitor中的指针指向自己。
今天就学到这里了!收工!
MarkWord和Synchronized的锁升级机制详解(JDK8)
锁升级机制在JDK 后已经废弃,本文所述仅为面试中常问的低版本synchronized的锁升级机制,具体新机制需查阅最新JDK源码。
在Java并发编程中,synchronized是最常用的关键字,用于保护代码块和方法在多线程场景下的并发安全问题。synchronized锁基于对象实现,通常用于修饰同步方法和同步代码块。
下面给出一段简单的Java代码,包含三种synchronized的使用方法,通过反编译查看字节码,了解synchronized的实现原理。
修饰方法时,synchronized关键字会在方法的字节码中添加ACC_SYNCHRONIZED标志,确保只有一个线程可以同时执行该方法。synchronized修饰静态方法同样添加此标志。
修饰代码块时,synchronized关键字会在相应的空投糖果网源码指令区间添加monitorenter和monitorexit指令,JVM通过这两个指令保证多线程状态下的同步。
ACC_SYNCHRONIZED、monitorenter、monitorexit的解释,来源于官网介绍和chatgpt翻译。
方法级的synchronized隐式执行,通过ACC_SYNCHRONIZED标志区分,方法调用指令会检查此标志。调用设置ACC_SYNCHRONIZED的方法时,线程进入monitor,执行方法,并在方法调用正常完成或异常中断时退出monitor。
monitorenter指令尝试获取与对象相关联的monitor的所有权,monitorexit指令执行时,对象相关联的monitor的进入计数减1。
Monitor是Java中用于实现线程同步和互斥的机制,每个Java对象都与一个Monitor相关联,主要目的是确保在任何给定时间,只有一个线程能够执行与特定对象相关联的临界区代码。
ObjectMonitor是JDK 的HotSpot源码中定义的Monitor,其核心参数包括EntrySet、WaitSet和一个线程的owner。
Java对象与monitor关联,需要了解Java对象布局和对象头的相关知识。
在JDK 1.6之前,synchronized需要依赖于底层操作系统的Mutex Lock实现,导致效率低下。在JDK 1.6之后,引入了偏向锁与轻量锁来减小获取和释放锁的性能消耗。
锁升级分为四种状态:无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁,锁会随着线程的竞争情况逐渐升级,但锁升级是不可逆的。
偏向锁在没有其他线程竞争时,持有偏向锁的线程不会主动释放,偏向锁的释放时机是在其他线程竞争该锁时。
轻量级锁使用CAS操作,尝试将对象头部的锁记录指针替换为指向线程栈上的锁记录。轻量级锁的撤销意味着不再通过自旋的方式等待获取锁,而是直接阻塞线程。
重量级锁状态下,对象的头部会指向一个Monitor对象,该Monitor对象负责管理锁的获取和释放。
JDK 1.6及之后版本引入了自适应自旋锁、锁消除和锁粗化等锁优化策略,以进一步提升synchronized的性能。
自适应自旋锁根据前一次在相同锁上的自旋时间以及锁的持有者状态来动态决定自旋的上限次数。
锁消除是JVM在JIT编译期间进行的优化,通过逃逸分析来消除不可能存在共享资源竞争的锁。
锁粗化是通过将加锁范围扩展到整个操作序列的外部,降低加锁解锁的频率来减少性能损耗。
本文总结了JDK8中synchronized的锁升级机制,介绍了无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁的升级流程,以提升并发效率。
找到卡顿来源,BlockCanary源码精简分析
通过屏幕渲染机制我们了解到,Android的屏幕渲染是通过vsync实现的。软件层将数据计算好后,放入缓冲区,硬件层从缓冲区读取数据绘制到屏幕上,渲染周期是ms,这让我们看到不断变化的画面。如果计算时间超过ms,就会出现卡顿现象,这通常发生在软件层,而不是硬件层。卡顿发生的原因在于软件层的计算时间需要小于ms,而计算的执行地点则在Handler中,具体来说是在UI的Handler中。Android进程间的交互通过Binder实现,线程间通信通过Handler。
软件层在收到硬件层的vsync信号后,会在Java层向UI的Handler中投递一个消息,进行view数据的计算。这涉及到测量、布局和绘制,通常在`ViewRootImpl`的`performTraversals()`函数中实现。因此,view数据计算在UI的Handler中执行,如果有其他操作在此执行且耗时过长,则可能导致卡顿,我们需要找到并优化这些操作。
要找到卡顿的原因,可以通过在消息处理前后记录时间,计算时间差,将这个差值与预设的卡顿阈值比较。如果大于阈值,表示发生了卡顿,此时可以dump主线程堆栈并显示给开发者。实现这一功能的关键在于在Looper中设置日志打印类。通过`Looper.loop()`函数中的日志打印,我们可以插入自定义的Printer,并在消息执行前后计算时间差。另一种方法是在日志中添加前缀和后缀,根据这些标志判断时间点。
BlockCanary是一个用于检测Android应用卡顿的工具,通过源码分析,我们可以了解到它的实现逻辑。要使用BlockCanary,首先需要定义一个继承`BlockCanaryContext`的类,并重写其中的关键方法。在应用的`onCreate()`方法中调用BlockCanary的安装方法即可。当卡顿发生时,BlockCanary会通知开发者,并在日志中显示卡顿信息。
BlockCanary的核心逻辑包括安装、事件监控、堆栈和CPU信息的采集等。在事件发生时,会创建LooperMonitor,同时启动堆栈采样和CPU采样。当消息将要执行时,开始记录开始时间,执行完毕后停止记录,并计算执行时间。如果时间差超过预设阈值,表示发生了卡顿,并通过回调传递卡顿信息给开发者。
堆栈和CPU信息的获取通过`AbstractSampler`类实现,它通过`post`一个`Runnable`来触发采样过程,循环调用`doSample()`函数。StackSampler和CpuSampler分别负责堆栈和CPU信息的采集,核心逻辑包括获取当前线程的堆栈信息和CPU速率,并将其保存。获取堆栈信息时,通过在`StackSampler`类中查找指定时间范围内的堆栈信息;获取CPU信息时,从`CpuSampler`类中解析`/proc/stat`和`/proc/mpid/stat`文件的CPU数据,并保存。
总结而言,BlockCanary通过在消息处理前后记录时间差,检测卡顿情况,并通过堆栈和CPU信息提供详细的卡顿分析,帮助开发者定位和优化性能问题。
QEMU Monitor 使ç¨
ä»å½ä»¤è¡å¯å¨qemuä¹åï¼æé® Ctrl-a c å¯ä»¥è¿å ¥monitorï¼ä¹åå¯ä»¥æ§è¡å¾å¤æç¨çå½ä»¤ãå ·ä½å¯ä»¥åè /questions//connect-to-running-qemu-instance-with-qemu-monitor
åæ¥é æåï¼é®é¢å·²ç»æç½äºã devè®¾å¤ å¯ä»¥æ¯å¾å¤å½¢å¼ï¼å¨æ§è¡åæ° -serial dev éé¢è®²çå¾å ¨é¢ã /p/ef3 ã
synchronize底层原理
synchronize底层原理是什么?我们先通过反编译下面的代码来看看Synchronized是如何实现对代码块进行同步的:
1 package com.paddx.test.concurrent;
2
3 public class SynchronizedDemo {
4 public void method() {
5 synchronized (this) {
6 System.out.println(Method 1 start);
7 }
8 }
9 }
反编译结果:
关于这两条指令的作用,我们直接参考JVM规范中描述:
monitorenter :
Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:
If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
If another thread already owns the monitor associated with objectref, the thread blocks until the monitors entry count is zero, then tries again to gain ownership.
这段话的大概意思为:
每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程:
1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
monitorexit:
The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.
The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.
这段话的大概意思为:
执行monitorexit的线程必须是objectref所对应的monitor的所有者。
指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。
通过这两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
我们再来看一下同步方法的反编译结果:
源代码:
1 package com.paddx.test.concurrent;
2
3 public class SynchronizedMethod {
4 public synchronized void method() {
5 System.out.println(Hello World!);
6 }
7 }
反编译结果:
从反编译的结果来看,方法的同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。
FreeBSD 7.3 安装GNOME图形界面
FreeBSD被认为是自由操作系统中的不知名的巨人。它不是Unix,但如Unix一样运行,具有兼容Unix的系统API。作为一个操作系统,FreeBSD被认为相当稳建可靠。FreeBSD默认是无桌面环境的命令行界面,想要使用桌面环境必须自行安装,或是使用PC-BSD之类的桌面发行版。
通常安装 FreeBSD 选择推荐方式的最小安装,安装完成后再通过编译源码或 pkg_add 命令安装其它软件。一般很少用到图形界面,但有时候有总比没有的好。
安装过程中,创建默认一个普通用户并指定组为 wheel ,因为 ssh 远程登录禁用的 root 用户并且只有 wheel 组的用户可以 su 到 root 用户。
下面安装图表界面:
通过 sysinstall 命令安装 gonme 和 Xorg
复制代码
代码如下:
Configure - Packages - CD/DVD - gnome - gonme2-2..2_1
Configure - Packages - CD/DVD - X - Xorg-7.4_3
安装完成后,不能直接运行 startx 命令,否则启动图形界面后就系统无响应。
先编辑 /etc/rc.conf 文件,增加
复制代码
代码如下:
dbus_enable=”YES”
hald_enable=”YES”
重新启动后,如果出现 “acd0: FAILURE - unknown CMD ...” 的错误信息,执行 hal-disable-polling --device /dev/acd0 命令即可。
再运行 startx 命令,至少我们可以使用 Xorg 的简易的图形界面。
如果使用 gnome 界面,执行 echo "exec gnome-session" .xinitrc
如果使用 kde 界面,执行 echo "exec startkde" .xinitrc
再运行 startx 即可
我们还可以进一步定制图形界面属性,利于配置刷新率、分辨率等等
使用 root 用户运行 Xorg -configure 生成 xorg.conf.new 文件
复制代码
代码如下:
Section “Monitor”
Identifier “Monitor0″
VendorName “Monitor Vendor”
ModelName “Monitor Model”
HorizSync -
VertRefresh -
EndSection
Section “Screen”
Identifier “Screen0″
Device “Card0″
Monitor “Monitor0″
DefaultDepth
SubSection “Display”
Viewport 0 0
Depth
Modes “×″
EndSubSection
EndSection
再运行 Xorg -config xorg.conf.new -retro 进行测试
如果出现灰格子和 X 形鼠标,Ctrl+Alt+Backspace 退出
测试完成后,运行 cp xorg.conf.new /etc/X/xorg.conf 即可更新
安装 VMware tools 时需要compat6x
compat6x-i-6.4...tbz
在FreeBSD 7.3 安装 GNOME 图形界面
启动默认的 FTP 服务器,编辑 /etc/rc.conf 文件
复制代码
代码如下:
ftpd_enable="YES"
再运行 /etc/rc.d/ftpd start
上传文件后,通过 pkg_add compat6x-i-6.4...tbz 安装后即可安装 VMware tools
运行 mount /dev/acd0 挂载光盘,安装文件挂载到 /cdrom 目录
运行 tar zxvf /cdrom/vmware-freebsd-tools.tar.gz -C /root/ 将安装文件解压到 /root 目录
运行 ./vmware-install.pl 命令后,N 次回车安装完成。
小结:
关于FreeBSD 7.3 安装GNOME图形界面的内容介绍完了,希望通过FreeBSD 的学习能对你有所帮助!
Open Hardware Monitor好ä¸å¥½
Open Hardware Monitorå°±æ¯è¿ä¹ä¸æ¬¾å¼æ¾æºä»£ç çå è´¹ç硬件çæ§è½¯ä»¶ï¼ç®åææ°ççæ¬ä¸º0.4 Betaï¼å°½ç®¡å¹¶éåååå¸ï¼ä¸è¿æ们è§å¾è¿æ¯æä»ç»ç»å¤§å®¶çæä¹ï¼æéæ±çæåå¯ä»¥ä¸è½½çåã
Open Hardware Monitorå¯ä»¥æ¯æ大é¨å常è§ç主æ¿è¯çä¸ç¡¬ä»¶ç»ä»¶ï¼çæ§å å«ä¸»æ¿ãCPUã硬çãæ¾ç¤ºå¡ãè£ ç½®ççµåã温度ãé£æ转éä¸è¿ä½é¢çãå³æ¶è´è½½çä¿¡æ¯ã
é¤äºå¨ä¸»è§çªä¸æ¾ç¤ºå个主è¦ç¡¬ä»¶ççæ§èµè®¯ä¹å¤ï¼Open Hardware Monitorè¿æä¾äºä¸ä¸ªæ¾ç¤ºæ¸©åº¦ååçå³æ¶å¾è¡¨ãå¦æä½ æéè¦ä¸ç´çæ§ç温度æCPUãGPU çå³æ¶å¨æï¼ä¹å¯ä»¥å¼å¯æ¡é¢âGadgetâå°å·¥å ·ï¼ç´æ¥å¨æ¡é¢ä¸æ¾ç¤ºææ°çæ§ä¿¡æ¯ä¸ç¡¬ä»¶è¿è¡ç¶æã
ä½ä¸ºä¸æ¬¾å¼æºè½¯ä»¶ï¼Open Hardware Monitoré¤äºæä¾å¯¹åä½Windows XP/Vista/7ç³»ç»çæ¯æï¼è¿å¯¹xå¹³å°çLinuxç³»ç»ä¹æä¾äºæ¯æã