电商网站建设教学总结,电商网站设计制作,top域名,郑州seo阿伟目录
前言
一.synchronized
1.1概念
1.2Synchronized是什么锁#xff1f;
1.3Synchronized加锁工作过程
1.4其他优化操作
二.死锁
2.1什么是死锁
2.2死锁的几个经典场景
2.3死锁产生的条件
2.4如何解决死锁 #x1f381;个人主页#xff1a;tq02的博客_CSDN博客…目录
前言
一.synchronized
1.1概念
1.2Synchronized是什么锁
1.3Synchronized加锁工作过程
1.4其他优化操作
二.死锁
2.1什么是死锁
2.2死锁的几个经典场景
2.3死锁产生的条件
2.4如何解决死锁 个人主页tq02的博客_CSDN博客-C语言,Java,Java数据结构领域博主 本文由 tq02 原创首发于 CSDN 本章讲解内容多线程的策略锁、CAS和JUC 多线程学习专栏多线程学习专栏 其他学习专栏 C语言 JavaSE MySQL基础 前言 在多线程的讲解当中我们可以知道synchronized是加锁操作让两个线程发生互斥效果在代码中使用synchronized关键字来实现锁的获取和释放。如果是刚刚接触多线程的人我希望你可以从第一章多线程开始学习http://t.csdn.cn/0vEhY 一.synchronized
1.1概念 Synchronized是Java中内置的锁机制用于实现线程同步。它可以通过在代码中使用synchronized关键字来实现锁的获取和释放。Synchronized关键字可以用在方法上或者代码块中。当一个线程执行到synchronized修饰的代码块时它会尝试获取锁如果锁没有被其他线程占用则获取成功执行代码块中的内容。如果锁已经被其他线程占用则该线程会进入等待状态直到获取到锁才能继续执行。
1.2Synchronized是什么锁
开始时是乐观锁, 如果锁冲突频繁, 就转换为悲观锁.开始是轻量级锁实现, 如果锁被持有的时间较长, 就转换成重量级锁.实现轻量级锁的时候大概率用到的自旋锁策略是一种不公平锁是一种可重入锁不是读写锁
注需要使用公平锁建议使用ReentrantLock来实现。ReentrantLock提供了公平锁和非公平锁两种模式通过构造函数的参数来指定锁的模式。
1.3Synchronized加锁工作过程 对于锁资源只有一个或者两个线程交替竞争的仍然需要使用系统调用无疑对CPU资源是极大的消耗。因此在jdk1.6针对Synchronized加锁进行了优化。按对锁的竞争程度划分成无锁偏向锁轻量级锁重量级锁。简单而言就是从无锁--重量级锁。 无锁 当你添加了锁时如果编译器认为不需要加锁会自动删除因此便是无锁 偏向锁 偏向锁不是真的 加锁, 只是给对象头中做一个 偏向锁的标记, 记录这个锁属于哪个线程.如果后续没有其他线程来竞争该锁, 那么就不用进行其他同步操作了(避免了加锁解锁的开销)如果后续有其他线程来竞争该锁(刚才已经在锁对象中记录了当前锁属于哪个线程了, 很容易识别当前申请锁的线程是不是之前记录的线程), 那就取消原来的偏向锁状态, 进入一般的轻量级锁状态.注相当于做个标记相当于 延迟加锁 . 能不加锁就不加锁, 尽量避免不必要的加锁开销. 轻量级锁 随着其他线程进入竞争, 偏向锁状态被消除, 进入轻量级锁状态(自适应的自旋锁). 此处的轻量级锁就是通过 CAS 来实现 通过 CAS 检查并更新一块内存 (比如 null 该线程引用)如果更新成功, 则认为加锁成功如果更新失败, 则认为锁被占用, 继续自旋式的等待(并不放弃 CPU). 注此处的自旋锁不会一种持续进行而是达到一定的时间/重试次数, 就不再自旋了. 重量级锁 如果锁竞争进一步激烈, 自旋不能快速获取到锁状态, 就会膨胀为重量级锁 此处的重量级锁就是指用到内核提供的 mutex . 具体流程 执行加锁操作, 先进入内核态.在内核态判定当前锁是否已经被占用如果该锁没有占用, 则加锁成功, 并切换回用户态.如果该锁被占用, 则加锁失败. 此时线程进入锁的等待队列, 挂起. 等待被操作系统唤醒.经历了一系列的沧海桑田, 这个锁被其他线程释放了, 操作系统也想起了这个挂起的线程, 于是唤醒这个线程, 尝试重新获取锁 1.4其他优化操作 我们额外补充2个编译器对锁的优化操作。锁消除和锁粗化
锁消除 代码中, 用到了 synchronized, 但其实没有在多线程环境下. (例如 StringBuffer)
StringBuffer tq02 new StringBuffer();
tq02.append(a);
tq02.append(b);
tq02.append(c);
tq02.append(d);每个 append 的调用都会涉及加锁和解锁. 但如果只是在单线程中执行这个代码, 那么这些加 锁解锁操作是没有必要的, 白白浪费了一些资源开销.因此将锁给优化了。
锁粗化
锁的粗化是根据锁的粒度粗和细 实际开发过程中, 使用细粒度锁, 是期望释放锁的时候其他线程能使用锁.但可能并没有其他线程来抢占这个锁. 这种情况 JVM 就会自动把锁粗化, 避免频繁申请释放锁.
二.死锁
2.1什么是死锁 死锁是指在多进程系统中每个进程都在等待某个资源而该资源又被其他进程占用导致所有进程都无法继续执行的状态。 例如A、B、C、D和E去上厕所A进入厕所并且锁门B.C.D等待可是A刚刚进入厕所因为特殊的原因凭空转移到了外面A就得重新排队可是门还是锁着的啊因此导致了死锁。
2.2死锁的几个经典场景 经典场景有 一个线程一把锁两个线程两把锁多个线程多把锁 1.一个线程一把锁 一个线程连续被同一个加锁两次如果是不可重入锁那么会是死锁。
解析我去上厕所我把厕所门锁住再通过厕所的窗户出去然后再来上厕所发现厕所锁住了就耐心等待却没想过这是自己锁的。
代码实现
public class Counter {void increase() { synchronize(this){increase() //可以理解为翻窗逃走第二次加锁时是锁了的}
}public static void main(String[] args) throws InterruptedException {final Counter counter new Counter();Thread t1 new Thread(() - {counter.increase();});t1.start();}
}
2.两个线程两把锁 线程1先获取锁A再尝试获取锁B同时线程2先获取锁B再尝试获取锁A此时两个线程就会互相僵住谁都获取不到对方持有的锁。
解析我在汽车里车钥匙在我妻子手上我出不来我妻子在房间里房间钥匙在我手上我妻子也出不来导致双方被锁导致死锁。
代码示例
public class Test {public static void main(String[] args) {//2个锁对象Object lockerA new Object();Object lockerB new Object();Thread t1 new Thread(() - {System.out.println(t1尝试获取锁A);synchronized (lockerA){System.out.println(t1获取到锁A);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(t1尝试获取锁B);synchronized (lockerB){System.out.println(t1获取到锁B);}}});Thread t2 new Thread(() - {System.out.println(t2尝试获取锁B);synchronized (lockerB){System.out.println(t2获取到锁B);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(t2尝试获取锁A);synchronized (lockerA){System.out.println(t2获取到锁A);}}});t1.start();t2.start();}
}
3.多个线程多把锁 很明显啊两把锁两个线程也有问题更何况是多把锁啊在这方面最经典的是哲学家就餐问题。 如图火柴人是哲学家、红线是筷子每一个哲学家的左右都有一根筷子。规定当有一根哲学家饿了会先拿起左边的筷子然后再拿右边的筷子吃完了就放下筷子。
造成死锁问题每一个哲学家都饿了然后都拿起了左边的筷子可是当拿右边的筷子时发现有其他人在使用所以导致阻塞然后一直等待别人吃饱放下筷子可是每个人都在等待。 2.3死锁产生的条件
死锁产生需要四个条件
互斥使用即当资源被一个线程使用(占有)时别的线程不能使用不可抢占资源请求者不能强制从资源占有者手中夺取资源资源只能由资源占有者主动释放。请求和保持即当资源请求者在请求其他的资源的同时保持对原有资源的占有。循环等待即存在一个等待队列P1占有P2的资源P2占有P3的资源P3占有P1的资源。这样就形成了一个等待环路。
当上述四个条件都成立的时候便形成死锁。当然死锁的情况下如果打破上述任何一个条件便可让死锁消失。
2.4如何解决死锁 想没有死锁那么我们可以从死锁产生的条件入手只有破坏其他一条就可以了。 互斥使用和不可抢占是锁的基本特性因此无法干预但是请求和保持也不可能改变因为这是代码执行逻辑。因此只有循环等待我们可以打破 为了解决死锁问题可以采取预防、避免、检测和解除四种方法。
预防通过设置某些限制条件以防止死锁的发生。
避免系统在分配资源时根据资源的使用情况提前作出预测从而避免死锁的发生。
检测允许系统在运行过程中产生死锁但系统中有相应的管理模块可以及时检测出已经产生的死锁并精确地确定与死锁有关的进程和资源然后采取适当措施清除系统中已经产生的死锁。
解除当发现有进程死锁后立即解脱它从死锁状态中出来。常用的方法包括剥夺资源和撤销进程。剥夺资源是从其他进程中剥夺足够数量的资源给死锁进程以解除死锁状态。撤销进程可以直接撤销死锁进程或撤销代价最小的进程直至有足够的资源可用从而消除死锁状态。 ---------------------懒惰的tq02