打算记录自己的学习情况,尽量不摆烂,另外一件事要有始有终,要弄完
如果多个线程处理同一个变量,读跟写都保证不了
2024.7.22》》》》》》》》》》》》
2.1.1volatile的实现原理
volatile不会引起线程上下文的切换和调度
一致性更新:处理器修改完某个变量会告诉其他处理器让它们更新最新版本的值,保证信息的一致
排他锁:某个处理器获得锁,就不让其他处理器操作这个变量。就是synchronized保证写后读,这不是2.1的重点
volatile能保证,当一个处理器写了某个被volatile修改的数据,其他处理器能正确的读到被修改后的值,就是保证读的正确
(小明):那写呢?不保证了?
不保证了,所以要充分理解volatile和syronchized,让它们各司其职,只盲目使用syronchized就有性能的大问题
内存屏障:屏障屏障,反正就是不让访问。内存的某个变量根据操作系统的某个约定使得在做一些处理变量的时候不让别人访问。可能有一个int version = 1;如果1就让访问,0不让访问
反正就是不让访问了。我感觉呢,应该屏障也是通过锁了去那个变量的导线,导致后面所有想访问这个变量,都得等着这根线没人用才可以。嗯,这也是锁的底层原理
缓冲行,跟cache沾边。当多个处理器操作缓存时会走一根大导线。处理器发送任何指令都是电压传输,如果2个处理器同时发命令两个电压造成电压叠加必定会造成混乱,最好情况就是其中一个处理器的指令执行被吃了(这也很恐怖),如果两个电压抵消,两个处理器的指令白干活。所以通过一些方法(应该也是锁),我走这根线你就先别走了,你在我后面,等我执行完你再执行。
缓存行由多个缓存线组成,如果某个处理器操作某个缓存线,别的处理器你就先等会。排队才有规则性
原子操作:要么都成功要么都失败,打游戏辛苦几个小时最后一步没存档。都回滚到存档开始数据重新打。这里侧重于某些指令希望一块都执行完并且不能有错
缓存行填充,缓存拿需要的变量时会去内存读,但是会一口气加载一个缓存行,也就是缓存行有64bit但是想要的变量就8bit,它会把剩下的56bit根据那个变量地址后面加载56bit。这个策略可能是好事,比如long[] 数组一口气没准都加载进去了。但是如果有a变量和b变量都需要操作,且b变量的操作要在a变量操作后执行,那就坏了,b和a绑在一块完全没法弄。所以希望某些操作的变量的bit很大。比如boolean。它放进去64bit占满了。
缓存行填充就是读内存地址会一口气加载一个缓存行的大小,64bit,32bit。然后某些变量可能有顺序关系,所以进行优化把一些关键的变量bit变的很大甚至填满
缓存命中:缓存离cpu近,内存离cpu远,如果缓存就有内存的数据就操作缓存而不是操作内存数据
写命中:首先,由于缓存命中,cpu会更“青睐”缓存行,跟缓存行打交道。操作完的数据写回缓存行。写了蛮多数据后,根据程序缓存行会把数据给内存的数据,缓存行再等着cpu继续操作数据。这也是计算机的理想操作数据场景
写缺失:当缓存行想往回更新内存数据时,内存的那块变量区域没了。迷失的孩子找不到回去的路。这就是写缺失。比如内存变量是一个即将垃圾回收的没用变量,操作这个变量更新内存数据时,已经被jvm回收,‘死了’,那就写缺失了。或者无法更新数据,已经到了家门口但是家里人不给你开门,也会写缺失
可见性:多个线程对同一个变量进行修改,能读到其他处理修改之后正确的数据
可见性 = volatile干的活 = 一致性更新 = 某个变量被处理器写后,其他处理器会嗅探到新数据并重新读新数据的值。
保障读,不保障写
这里在讲啥呢?
录制_2024_07_22_21_53_12_901
哇趣,上传视频后简直糊到爆炸,家人们先凑活看吧。后面有办法我再补回来
这里都能看懂了,重点是理解清楚视频,这里的‘嗅探’,‘一致性’。都是刚刚讲的清楚明白的概念。
在这里,通过某个处理器修改更新值后,其他处理器马上放弃自己处理的值重新读值再处理。这也是有些写后读的思想(其实整本书全部都是为了实现写后读,就是怎么实现的了)
2.1.2volatile的优化
其实就是刚刚讲的缓存行填充,8bit的东西结果后面56bit全放进去了,万一中间有一些逻辑。那就完了。所以防止这种情况干脆把关键需要的变量放大甚至占满。
2.2synchronized原理
锁方法,锁的是实例化的对象。
锁静态方法,锁类
锁方法块,锁对于锁的那个对象
通过读取对象头的信息知道是不是要上锁,上什么锁。
知道要上锁后,就用monitor对象锁东西。分成monitorenter和monitorexit两个对象放到开始和结束
家人们,觉得写的好就用去大厂的无敌代码小手点个赞,俺看到了会觉得自己做到事是有意义的,会更有决心更完。评论收藏都可以