网页游戏网站大全突袭,登錄wordpress界面,河北网站建站制作,帝国cms影视网站模板文章目录 乐观锁和悲观锁重量级锁和轻量级锁挂起等待锁和自旋锁公平锁和非公平锁可重入锁和不可重入锁读写锁相关面试题 锁#xff1a;非常广义的概念#xff0c;不是指某个具体的锁#xff0c;所有的锁都可以往这些策略中套 synchronized#xff1a;只是市面上五花八门的锁… 文章目录 乐观锁和悲观锁重量级锁和轻量级锁挂起等待锁和自旋锁公平锁和非公平锁可重入锁和不可重入锁读写锁相关面试题 锁非常广义的概念不是指某个具体的锁所有的锁都可以往这些策略中套 synchronized只是市面上五花八门的锁种其中一种典型的实现Java 内置的推荐使用的锁
乐观锁和悲观锁
这两个词不是指某个具体的锁而是锁的一种“特性”描述了“一类”
乐观锁加锁的时候假设出现冲突的概率不大
接下来围绕加锁要做的工作就会更少 悲观锁加锁的时候假设出现锁冲突的概率很大接下来围绕加锁要做的工作就会更多
使用 synchronized初始情况下是乐观的预估接下来锁冲突概率不大
同时会在背后偷偷地统计锁冲突了多少次如果发现锁冲突达到一定程度了就会转变为“悲观的” 乐观锁和悲观锁需要做的事情是不同的乐观做的事情少一点悲观做的事情往往更重量级 站在预测锁冲突的概率是否高 synchronized 是自适应的 重量级锁和轻量级锁
效果和悲观乐观是重叠的只是站在的角度不一样
重量级锁加锁的开销比较大要做的工作更多
往往悲观的时候会做的重 轻量级锁加锁的开销比较小要做的工作相对更少往往乐观的时候会做的轻 但也不能认为是 100%等价因为乐观和悲观是站在“预估所冲突”角度重量轻量是站在“加锁开销“角度 站在加锁的开销角度 synchronized 也是自适应的 挂起等待锁和自旋锁
挂起等待锁属于是悲观锁/重量级锁的一种典型实现 自旋锁乐观锁/轻量级锁的一种典型实现 比如 你去追你的女神 女神女神我好喜欢你 你尝试对女神加锁 女神表示我有男朋友了 女神表示她这把锁已经被别的线程给加了 你就可以选择“等待”等到女神锁被释放比如 你选择每天仍然会给女神不停地问候“早安午安…”这里的行为称为“自旋锁” 这里的等待是“忙等”等待的过程中 CPU 的资源不会释放某天女神和男朋友吵架了不开心你就立刻能感知到机会来了。这样你就可以在女神锁释放的第一时间立刻抓住机会能够上位不停地循环地检测锁是否被释放一旦锁释放就能立即有机会能获得锁 你选择把女神拉黑先不联系了若干年后你从别人那里听说女神分手了你再去联络女神这种行为就是“挂起等待锁” 不联系就相当于“让出 CPU 资源”CPU 就可以去做别的事了不理女神之后我们就可以有心思好好学习好好敲代码好好找工作了在过程中做成更多的事情。过了一段时间后我们通过一些途径听说女神分手了再伺机而动但“听说”的时效性很低这个中间可能有很长的时间跨度。在这个时间跨度里女神是否由谈了男朋友分手了多少次你是不知道的挂机时间更长但能节省下 CPU 资源去做别的事情 注意
只有在假定锁冲突概率不高的情况下才能“忙等”。如果好几个线程都在竞争同一个锁一个线程拿到锁其他的都在“忙等”这样总的 CPU 消耗就会非常高而且由于竞争太激烈导致有些线程要等待很久才能拿到锁锁冲突很高的概率很高的话就不适合“自旋锁”方案 挂起等待锁也就适合“悲观锁”这样的场景了锁竞争非常激烈预测拿到锁的概率本身就不打不放吧 CPU 让出来充分的做其他事情 synchronized“自适应” 轻量级锁就是基于自旋的方式实现的JVM 内部用户态代码实现重量级锁就是基于挂起等待的方式实现的调用操作系统 API在内核中实现 纯用户态代码往往执行效率比内核态代码的高一些总体来说我们还是认为“自旋”的效率更高的但是 CPU 开销更大 挂起等待锁的操作虽然 CPU 开销变少了但整体的等待时间更多 公平锁和非公平锁
日常生活中说的公平可能有不同的含义 当女神分手了该轮到谁上位呢 公平锁 在计算机中约定“先来后到”为公平 非公平锁 系统原生 synchronized 属于非公平锁
当 N 个线程竞争同一个锁其中一个线程拿到锁了后续该线程释放锁之后剩下的 N-1 个线程就要重新竞争锁谁能拿到锁就不一定了当然也不能保证这些线程竞争中获取的概率一定是数学上的严格均等 本身操作系统内核里针对锁的处理就是如此synchronized 在系统内核的基础上没有做啥额外的操作 如需要使用公平锁就需要做额外的工作
比如引入队列记录每个线程加锁的顺序 可重入锁和不可重入锁
死锁问题如果一个线程针对同一把锁连续加锁两次就可能出现死锁如果把锁设为“可重入”就可以避免死锁了 可重入是专门的计算机术语不要写作“可重复”这样的词 可重入锁
会记录当前是哪个线程持有了这把锁在加锁的时候判定当前申请锁的线程是否就是锁的持有者计数器记录加锁的次数从而确定何时真正释放锁 遇到一个 { 加一次锁计数器加一遇到一个 } 解锁一次计数器减一等到计数器为零真正释放锁 读写锁
synchronized 并非是读写锁 所谓的读写锁把“加锁操作”分为两种情况
读加锁写加锁 如果多个线程同时读这个变量没有线程安全问题但是一个线程读/一个线程写两个线程都写 就会产生问题 在实际开发中在大部分场景下读操作的频次本身就比写操作的频次高。所以就让读操作不产生锁冲突这样就只有少数写操作会产生冲突这样效率就高了 读写锁提供了两种加锁的 API系统内置的锁Java 标准库中的读写锁类为ReentrantReadWriteLock
加读锁 ReentrantReadWriteLock 的内部类 ReentrantReadWriteLock.ReadLock 表示加读锁这个对象提供了 lock/unlock ⽅法进⾏加锁解锁 加写锁 ReentrantReadWriteLock 的内部类 ReentrantReadWriteLock.WriteLock 表示加写锁这个对象也提供了lock/unlock ⽅法进⾏加锁解锁 解锁的 API 是一样的就需要把 unlock 放到 finally 中确保能够执行到 如果两个线程都是按照读方式加锁此时不会产生锁冲突如果两个线程都是家写锁此时会产生锁冲突如果一个线程读锁一个线程写锁也会产生冲突 相关面试题