广东手机网站制作公司沈阳网站建设培训学校
- 作者: 五速梦信息网
- 时间: 2026年04月20日 11:05
当前位置: 首页 > news >正文
广东手机网站制作公司,沈阳网站建设培训学校,做淘客网站需要营业执照吗,手机能看的网站一、#x1f49b; 锁策略——接上一篇 6.分为可重入锁#xff0c;不可重入锁 如果一个线程#xff0c;针对一把锁#xff0c;连续加锁两次#xff0c;会出现死锁#xff0c;就是不可重入锁#xff0c;不会出现死锁#xff0c;就是可重入锁。 如果一个线程#xff0c;针… 一、 锁策略——接上一篇 6.分为可重入锁不可重入锁 如果一个线程针对一把锁连续加锁两次会出现死锁就是不可重入锁不会出现死锁就是可重入锁。 如果一个线程针对一把锁连续加锁两次如果产生了死锁就是不可重入锁 public class Demo5 {public static void main(String[] args) {Thread tnew Thread(new Runnable() {int count0;Overridepublic synchronized void run() { //加了一层锁synchronized (this){ //加了第二次锁count;}System.out.println(count);}});t.start();}
}那么我们来解释一下什么叫做死锁呢 public synchronized void run() { //加了一层锁 synchronized (this){ //加了第二次锁 count; } 这个代码中调用方法先针对this加锁此时假设加锁成功了接下来到往下执行代码块中的this来进行加锁此时就会出现锁竞争this已经处于锁状态了此时该线程就会阻塞一直阻塞到锁被释放才能有机会拿到锁。 这也是死锁第一个体现this这个锁必须要run执行完毕才能释放但是要想执行完事这个第二次加锁就应该加上方法才可以执行但是第二次想加上第一个就应该放锁所以由于this锁没法释放代码就卡在这里了因此线程数量就僵住了。 还好synchronized是可重入锁JVM帮我们承担了很多的任务 这里卡死就很不科学的一种情况第二次尝试加锁的时候该线程已经有了这个锁的权限了这个时候不应该加锁失败不应该进行阻塞等待的 不可重入锁这把锁不会保存哪个线程加上的锁只要他当前处于加锁状态之后收到了‘加锁的请求’就会拒绝当前加锁而不管当下线程是哪个就会产生死锁。 可重入锁会让这个锁保存是哪个线程加的锁后续收到加锁请求之后就会先对比一下看看加锁的线程是不是当前持有自己这把锁的线程这个时候就可以灵活判定了。 那么该如何对比捏 synchronized(this){synchronized(this){synchronized(this){
······-执行到这个代码出了这个代码刚才加上的锁是否要释放} 如果最里面的释放了锁意味着最外面的synchronized和中间的synchronized后
} 续的代码部分就没有处在锁的保护之中了 真正要在这个地方释放锁如加锁N层遇到了 } , JVM如何知道是最后一个呢整一个整型变量记录当前这个线程加了几次锁每遇到一个加锁操作计数器1每遇到一个解锁操作就-1当计数器减为0时才真正执行释放锁操作其他时候时不释放的。这一个思想就叫做‘引用计数’脑力10000人类进化不带我 注补充静态方法是针对类加锁普通方法是针对this加锁 二、 死锁的详细介绍两次加锁都是同一个线程 死锁的三种典型情况 1.一个线程一把锁但是不可入锁该线程针对这个锁联系加两次就会出现死锁。 2.两个锁两个锁这两个县层先分别获取一把锁然后再尝试分别获取对方的锁 比如我拿了酱油要炫饺子小杨拿醋我让他把醋先给我然后我一起给你小杨一拍桌子凭啥先给你你多个啥如下图双方陷入死循环中 public class Demo5 {public static Object locker1new Object();public static Object locker2new Object();public static void main(String[] args) {Thread t1new Thread(new Runnable() {Overridepublic synchronized void run() {synchronized (locker1) { //给1加锁System.out.println(s1.start);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (locker2) { //没有放弃1的锁System.out.println(s2.over);}}}});t1.start();Thread t2new Thread(new Runnable() {Overridepublic synchronized void run() {synchronized (locker2) { //給2加锁System.out.println(t2.start);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (locker1) { //没有放弃2的锁System.out.println(t1.over);}}}});t2.start();}
}3.N个线程M把锁 哲学家就餐问题 三、 如何应该避免死锁呢先明确死锁产生的原因死锁的必要条件缺一不可 1.互斥使用一个线程获取到一把锁之后别的线程不能获取到这个锁。实际使用的锁一般都是互斥的锁的基本特性 2.不可抢占锁只能被持有者主动释放而不能是其他线程直接抢走也是锁的基本特性 3.请求和保持这一个线程尝试获取多把锁在获取第二把时候会保持对第一把的获取状态取决于代码结构比如刚才写的我只要让他获取完第一把再释放在获取第二把这样不发生冲突但是可能会影响需求。 4.循环等待t1尝试获取locker2,需要t2执行完释放locker2,t2尝试获取locker1,需要t1执行完毕释放locker1(取决于代码结构 我们的解决方式趋向于解决第四种打破循环等待如何具体实现解决死锁实际方法有很多 首先就是银行家算法杀牛刀了属于复杂没必要会 简单有效方法针对锁进行编号且规定加锁的顺序只要线程加锁的顺讯都严格执行上述顺序就没有循环等待。 如下 一般面试我们主动点 问到死锁捡着了细细的答给他讲让他觉得你是理解的 1.什么是死锁。 2.死锁的几个典型场景 3.死锁产生的必要条件 4.如何解决死锁的问题 四、 ❤️ synchronized具体采用了哪些锁策略呢 1.既是悲观锁又是乐观锁 2.既是重量级锁又是轻量级锁 3.重量级锁部分是基于多系统互斥锁实现的轻量级锁部分是基于自旋锁实现的 4.synchronized是非公平锁不会遵守先来后到锁释放之后哪个线程拿到锁个凭本事 5.synchronized是可重入锁内部会记录哪个线程拿到了锁记录引用计数 6.synchronized不是读写锁 synchronized-内部实现策略自适应 讲解一下自适应代码中写了一个synchhronized之后可能产生一系列自适应的过程锁升级锁膨胀 无锁-偏向锁-轻量级锁-重量级锁 偏向锁不是真的加锁而只是做了一个标记如果有别的线程来竞争锁才会真的加锁如果没有别的线程竞争就自始至终都不加锁了渣女心态没人来追你我就钓鱼你要是被追了我先给你个身份让别人别靠近你。——当然加锁本身也有一定消耗 偏向锁在没人竞争的时候就是一个简单的轻量的标记如果有别的线程来尝试加锁就立即把偏向锁升级成真正加锁让别人阻塞等待能不加锁就不加锁 轻量级锁-synchronized通过自旋锁的方式实现轻量级锁——这边把锁占据了另一个线程按照自旋的方式这个锁操作比较耗cpu,如果能够快速拿到锁多耗点也不亏来反复查询当前的锁状态是不是被释放但是后续如果竞争这把锁的线程越来越多了锁冲突更加激烈了从轻量锁升级到重量级锁随着竞争激烈即使前一个线程释放锁也不一定能够拿到锁何时能拿到时间可能比较久了会 锁清除编译器会智能的判断当前这个代码是否有必要加锁如果你写了加锁但实际没必要加锁就会自动清除锁 如单个线程使用StringBuffer编译器进行优化是保证优化之后的逻辑和之前的逻辑是一致的这样就会让代码优化变的保守起来咱们猿们也不能指望编译器优化来提升代码效率自己也要有作用判断何时加锁也是咱们非常重要的工作。 锁粗化 关于锁的粒度锁中操作包含代码多锁粒就大 //1号 全写的是伪代码
和2号比较明显是2号的粒度更大
for(
synchronized(this){count}
}//2号
synchronized(this){
for{
count}
} 锁粒大锁粒小各有好处 锁粒小并发程度会更高效率也会更快 锁粒大是因为加锁本身就有开销。如同打电话打一次就行老打电话也不好 上述的都是基本面试题 五、 CAS全称Compare and swap) 字面意思比较并且交换 能够比较和交换某个寄存器中的值和内存中的值看是否相等如果相等就把另一个寄存器中的值和内存进行交换 boolean CAS(address,expectValue,swapValue){if(addressexpectValue){ //这个相当于C语言中的*看他两个是否相等addressswapValue; //相等就换值return true;
}return false; 此处严格的说是adress内存的值和swapValue寄存器里的值进行交换但是一般我们重点关注的是内存中的值寄存器往往作为保存临时数据的方式这里的值是啥很多时候我们选择是忽略的。 这一段逻辑是通过一条cpu指令完成的原子的或者说确保原子性给我们编写线程安全代码打开了新的世界。 CAS的使用 1.实现原子类多线程针对一个count,在java库中已经提供了一组原子类 java.util.concurrent(并发的意思.atomic AtomicInteger,AtomicLong,提供了自增/自减/自增任意值自减任意值··这些操作可以基于CAS按照无锁编程的方式来实现。 如for(int i0;i5000;i){ count.getAndIncrement(); //count count.incrementAndGet(); //count count.getAndDecrement(); //count– count.decrementAndGet() //–count } import java.util.concurrent.atomic.AtomicInteger;public class Demo6 {public static AtomicInteger countnew AtomicInteger(0); //这个类的初值呗public static void main(String[] args) throws InterruptedException {Thread t1new Thread(()-{for (int i0;i500;i){count.getAndIncrement();}});Thread t2new Thread(()-{for (int i0;i500;i){count.getAndIncrement();}});t1.start();t2.start();t1.join(); //注意要等待两个线程都结束再开始调用t2.join();System.out.println(count);}
}上述原子类就是基于CAS完成的 当两个线程并发的线程执行的时候如果加限制意味着这两个是串行的能计算正确的有时候者两个操作是穿插的这个时候是会出现问题的 加锁保证线程安全通过锁强制避免出现穿插 原子类/CAS保证线程安全借助CAS来识别当前是否出现穿插的情况如果没有穿插此时直接修改就是安全的如果出现了穿插就会重新读取内存中最新的值再次尝试修改。 部分源码合起来的意思就是 public int getAndIncrement(){int oldValuevalue; //先储存值防止别的线程偷摸修改之后无法恢复到之前的值while(CAS(Value,oldValue,OldValue1)!true){ //检查是否线程被别的偷摸修改了//上面的代码是Value是否等于oldValue,假如等于就把Value赋值OldValue1oldValuevalue; //假如修改了就恢复了原来的样子}return oldValue;} 假如这种情况刚开始设置value0, CAS是一个指令这个指令本身是不能够拆分的。 是否可能会出现两个线程同时在两个cpu上微观上并行的方式来执行CAS本身是一个单个的指令这里其实包含了访问操作当多个cpu尝试访问内存的时候本质也是会存在先后顺序的。 就算同时执行到CAS指令也一定有一个线程的CAS先访问到内存另一个后访问到内存 为啥CAS访问内存会有先后呢 多个CPU在操作同一个资源也会涉及到锁竞争指令级别的锁是比我们平时说的synchronized代码级别的锁要轻量很多cpu内部实现的机制
- 上一篇: 广东事业单位网站无锡网站建设推荐智勇
- 下一篇: 广东双语网站建设多少钱wordpress搬家后错乱
相关文章
-
广东事业单位网站无锡网站建设推荐智勇
广东事业单位网站无锡网站建设推荐智勇
- 技术栈
- 2026年04月20日
-
广东石油化工建设集团网站网站怎么自己做优化
广东石油化工建设集团网站网站怎么自己做优化
- 技术栈
- 2026年04月20日
-
广东省住房和城乡建设厅网站 粤建网工业设计最吃香的专业
广东省住房和城乡建设厅网站 粤建网工业设计最吃香的专业
- 技术栈
- 2026年04月20日
-
广东双语网站建设多少钱wordpress搬家后错乱
广东双语网站建设多少钱wordpress搬家后错乱
- 技术栈
- 2026年04月20日
-
广东外贸网站建设展示型企业网站设计方案
广东外贸网站建设展示型企业网站设计方案
- 技术栈
- 2026年04月20日
-
广东网络公司网站建设wordpress怎么解密密码
广东网络公司网站建设wordpress怎么解密密码
- 技术栈
- 2026年04月20日
