(1)一个人只要自己不放弃自己,整个世界也不会放弃你.
(2)天生我才必有大用
(3)不能忍受学习之苦就一定要忍受生活之苦,这是多么痛苦而深刻的领悟.
(4)做难事必有所得
(5)精神乃真正的刀锋
(6)战胜对手有两次,第一次在内心中.
(7)好好活就是做有意义的事情.
(8)亡羊补牢,为时未晚
(9)科技领域,没有捷径与投机取巧。
(10)有实力,一年365天都是应聘的旺季,没实力,天天都是应聘的淡季。
(11)基础不牢,地动天摇
(12)写博客初心:成长自己,辅助他人。当某一天离开人世,希望博客中的思想还能帮人指引方向.
(13)编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~
【17】死锁与如何避免死锁
文章目录
1.死锁
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。
1.1产生死锁的三个必要条件
(1)多个操作者(M >=2) 争夺多个资源(N >=2),N <= M(资源数也要小于等于操作者数).
(2)争夺资源的顺序不一致;
(3)拿到资源不放手。
1.2学术化描述死锁
(1)需要满足互斥条件
(2)请求保持(已经拿到了一个资源,但是要拿到一个新的资源,而且我请求的资源被别的线程拿到了)
(3)不剥夺(线程所拿到的资源数不能够被剥夺)
(4)环路等待(一个操作者拿到了A等B,另一个操作者拿到了B等A)。
1.3死锁案例代码
package com.gdc.deadlock.lock;
public class DeadLockDemo {
//锁对象
private static Object no13 = new Object();
private static Object no14 = new Object();
//拿锁方法1
private static void zsDo() throws InterruptedException {
String threadName = Thread.currentThread().getName();
synchronized (no13){
System.out.println(String.format("%s get 13 lock",threadName));
Thread.sleep(100);
synchronized (no14){
System.out.println(String.format("%s get 14 lock",threadName));
}
}
}
private static void lsDo() throws InterruptedException {
String threadName = Thread.currentThread().getName();
synchronized (no14){
System.out.println(String.format("%s get 14 lock",threadName));
Thread.sleep(100);
synchronized (no13){
System.out.println(String.format("%s get 13 lock",threadName));
}
}
}
private static class PersonThread extends Thread{
private String name;
public PersonThread(String name) {
this.name = name;
}
@Override
public void run() {
Thread.currentThread().setName(name);
//调用方法进入同步块
try {
zsDo();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread.currentThread().setName("MainThread");
PersonThread personThread = new PersonThread("personThread");
personThread.start();
lsDo();
}
}
(1)主线程先拿锁对象13,再拿锁对象14.
(2)PersonThread先拿锁对象14,再拿锁对象13.
(3)以上zsDo与lsDo两个方法分别被主线程与PersonThread线程调用。
(4)在这种情况下就一定会产生死锁,因为如果不产生死锁,肯定会正常打印并执行完成,而实际运行时是两个线程卡住了。
(5)死锁不抛出异常,看不到任何信息的日志, 程序也没有死,但是线程却偏偏不做事情。
(6)程序发生了死锁,只能重新启动程序,用户只能将程序关闭,重新启动,这样还是个很严重的问题。
1.4如何避免死锁
打破以下两个条件的任意一个即可:
(1)争夺资源的顺序不对(争夺资源顺序保持一致);
1.4.1争夺资源顺序保持一致解决死锁
/**
* 通过让多个线程争夺资源的顺序保持一致解决死锁
*/
public class ResolveDeadLockDemo1 {
//锁对象
private static Object no13 = new Object();
private static Object no14 = new Object();
//拿锁方法1
private static void zsDo() throws InterruptedException {
String threadName = Thread.currentThread().getName();
synchronized (no13){
System.out.println(String.format("%s get 13 lock",threadName));
Thread.sleep(100);
synchronized (no14){
System.out.println(String.format("%s get 14 lock",threadName));
}
}
}
private static void lsDo() throws InterruptedException {
String threadName = Thread.currentThread().getName();
synchronized (no13){
System.out.println(String.format("%s get 13 lock",threadName));
Thread.sleep(100);
synchronized (no14){
System.out.println(String.format("%s get 14 lock",threadName));
}
}
}
private static class PersonThread extends Thread{
private String name;
public PersonThread(String name) {
this.name = name;
}
@Override
public void run() {
Thread.currentThread().setName(name);
//调用方法进入同步块
try {
zsDo();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread.currentThread().setName("MainThread");
PersonThread personThread = new PersonThread("personThread");
personThread.start();
lsDo();
}
}
(2)拿到资源不放手(拿到资源放手,可以使用Lock提供和API,可以用不同的方式拿锁,例如采用尝试拿锁的方式,即trylock)。
使拿锁的过程放手,使用Lock接口,使用尝试拿锁机制。
1.4.2Lock提供的API
/**
* 通过尝试拿锁解决死锁问题
* 场景:
* 1.主线程中先尝试拿No13 锁,再尝试拿No14锁,No14锁没拿到,连同No13 锁一起释放掉
* 2.子线程中先尝试拿No14锁,再尝试拿No13锁,No13锁没拿到,连同No14锁一起释放掉
*/
public class TryLock {
/**
* 第1个锁
*/
private static Lock No13 = new ReentrantLock();
/**
* 第2个锁
*/
private static Lock No14 = new ReentrantLock();
private static class PersonThread extends Thread{
/**
* 线程名
*/
private String name;
public PersonThread(String name) {
this.name = name;
}
@Override
public void run() {
Thread.currentThread().setName(name);
try {
secondToFirst();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 先尝试拿No13 锁,再尝试拿No14锁,No14锁没拿到,连同No13 锁一起释放掉
* @throws InterruptedException
*/
private static void firstToSecond() throws InterruptedException{
String threadName = Thread.currentThread().getName();
Random r = new Random();
while (true){
if(No13.tryLock()){
try {
System.out.println(String.format("%s get 13 lock",threadName));
if(No14.tryLock()){
try {
System.out.println(String.format("%s get 14 lock",threadName));
System.out.println("firstToSecond do work------");
break;
}finally {
No14.unlock();
}
}
}finally {
No13.unlock();
}
}
Thread.sleep(r.nextInt(3));
}
}
/**
* 先尝试拿No14锁,再尝试拿No13锁,No13锁没拿到,连同No14锁一起释放掉
* @throws InterruptedException
*/
private static void secondToFirst() throws InterruptedException{
String threadName = Thread.currentThread().getName();
Random r = new Random();
while (true){
//1.1.先尝试拿No14锁
if(No14.tryLock()){
System.out.println(String.format("%s get 14 lock",threadName));
//1.2.再尝试拿No13锁
try{
if(No13.tryLock()){
try {
System.out.println(String.format("%s get 13 lock",threadName));
System.out.println("secondToFirst do work------");
break;
}finally {
//1.3释放No13锁
No13.unlock();
}
}
}finally {
//1.4释放No14锁
No14.unlock();
}
}
//进入等待状态
Thread.sleep(r.nextInt(3));
}
}
public static void main(String[] args) {
Thread.currentThread().setName("MainThread");
PersonThread personThread = new PersonThread("personThread");
personThread.start();
try {
firstToSecond();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
(1)在同一时刻只会有一个线程能够同时成功拿到两把锁
(2)为什么要休眠一小段时间
Thread.sleep(r.nextInt(3));
(1)如果不休眠一小段时间,两个线程拿锁的时间就会变长,这在并发编程之中称为活锁。
(2)休眠一小段时间是为了让两个线程运行的时间错开,让线程有机会拿到CPU时间片获得执行。
1.4.3官方说法
(1)打破互斥条件:改造独占性资源为虚拟资源,大部分资源已无法改造。
(2)打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。
(3)打破占有且申请条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。
(4)打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。
1.5避免死锁常见的算法
避免死锁常见的算法有有序资源分配法、银行家算法。
1.6活锁
多个线程一直在拿锁释放锁的过程中,一直进行下去,这种情况就称为活锁。
(1)线程A B,锁1,2
(2)线程A(1)<2>首先拿到锁1,尝试着去拿锁2
(3)线程B(2)<2>首先拿到锁2,尝试着去拿锁1
(4)线程A(1) <2> 接下来将自己持有的锁释放掉,下一次循环里面,再一次拿到了锁1,尝试着去拿锁2.
(5)线程B(2) <1>尝试着去拿锁1,在不断的拿锁释放锁的过程中,把这种情况称之为活锁。
(6)设置休眠时间Thread.sleep(r.nextInt(3));
2.线程饥饿
老是拿不到CPU的时间片,得不到执行。需要将线程运行的时间错开一点点,让线程有机会获取CPU时间片,从而进入运行状态。
3.打赏鼓励
感谢您的细心阅读,您的鼓励是我写作的不竭动力!!!