【JAVA】:万字长篇带你了解JAVA并发编程-死锁优化【六】

目录

个人主页: 【⭐️个人主页

需要您的【💖 点赞+关注】支持 💯

JAVA并发系列文章

  1. 【JAVA】:万字长篇带你了解JAVA并发编程【一】-CSDN博客
  2. 【JAVA】:万字长篇带你了解JAVA并发编程-线程池【二】-CSDN博客
  3. 【JAVA】:万字长篇带你了解JAVA并发编程-并发集合【三】-CSDN博客
  4. 【JAVA】:万字长篇带你了解JAVA并发编程-线程安全【四】-CSDN博客
  5. 【JAVA】:万字长篇带你了解JAVA并发编程-并发设计模式【五】-CSDN博客
  6. 【JAVA】:万字长篇带你了解JAVA并发编程-死锁优化【六】-CSDN博客

【JAVA】:万字长篇带你了解JAVA并发编程-并发编程的优化【六】

并发编程的优化

避免死锁

在多线程编程中,死锁是一个常见的问题。当多个线程同时请求多个资源,并且这些资源相互依赖于对方释放资源时,就可能发生死锁。死锁会导致程序的停滞,进而影响系统的性能和可用性。

死锁产生的条件

  • 互斥条件:进程对所分配到的资源不允许其他进程进行访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源
  • 请求和保持条件:进程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他进程占有,此事请求阻塞,但又对自己获得的资源保持不放
  • 不可剥夺条件:是指进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用完后自己释放
  • 环路等待条件:是指进程发生死锁后,必然存在一个进程--资源之间的环形链死锁问题

避免死锁的方式

  1. 避免无谓的锁竞争

    无谓的锁竞争是指当线程获取到一个锁后,并不需要它继续保持该锁的所有权,但依然在持有锁的情况下等待其他资源的释放。这种情况下,如果其他线程也需要该锁,就可能导致死锁的发生。因此,在编写代码时,我们应该尽量避免无谓的锁竞争,只有在必要的时候才去获取和释放锁。

  2. 按顺序获取锁

    当多个线程需要获取多个锁时,为了避免死锁,我们可以约定一个获取锁的顺序,而且所有线程都按照这个顺序来获取锁。这样,即使出现了争抢资源的情况,由于按照相同的顺序获取锁,就不会发生循环等待的情况,从而避免了死锁的发生。

  3. 使用定时锁

    Java提供了一种定时锁的机制,即在尝试获取锁的时候,设定一个等待的时间,在超过这个时间之后,如果还没能获取到锁,就主动放弃锁。这样可以避免线程无限等待的情况,提高系统的可用性。

  4. 使用并发工具类

    在Java中,有很多并发工具类可以帮助我们更方便地处理多线程编程中的问题,避免死锁的发生。例如,使用ConcurrentHashMap代替Hashtable,使用ConcurrentLinkedQueue代替LinkedList等。这些并发工具类已经内部实现了线程安全的机制,可以有效地避免死锁的问题。

  5. 避免线程持有锁的时间过长

    当一个线程持有一个锁并长时间不释放时,就会阻塞其他线程的访问,并且增加了出现死锁的概率。因此,我们需要尽量缩短线程持有锁的时间,及时释放锁,以便其他线程能够及时获取锁并继续工作。

  6. 仔细设计资源申请顺序

    在设计多线程程序时,我们要仔细考虑资源申请的顺序。尽量避免一个线程同时申请多个资源,以免造成资源的竞争和死锁的发生。如果多个线程都需要获取同一组资源,可以考虑引入一个资源分配器,通过分配器来按照一定的策略来分配资源,避免资源的竞争。

  7. 使用避免死锁的算法

    在一些特殊情况下,即使遵循了以上的原则,仍然无法避免死锁的发生。这时,我们可以使用一些避免死锁的算法,例如银行家算法、资源分级算法等。这些算法可以通过动态地检测和避免死锁,保证系统的正常运行。

死锁例程代码

java 复制代码
/**
 * @author 孔翔
 * @since 2023-11-08
 * copyright for author : 孔翔 at 2023-11-08
 * java-study
 */
public class Deadlock {
 private static Object obj1 = new Object();
 private static Object obj2 = new Object();

 public static void main(String[] args) {
  new Thread(new Thread1()).start();
  new Thread(new Thread2()).start();
 }

 private static class Thread1 implements Runnable {
  @Override
  public void run() {
   synchronized (obj1) {
    System.out.println("Thread1 拿到 obj1 的锁");
    try {
     //停顿2秒的意义在于,让thread2线程拿到obj2的锁
     Thread.sleep(2000);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }

    synchronized (obj2) {
     System.out.println("Thread1 拿到 obj2 的锁");
    }
   }

  }
 }

 private static class Thread2 implements Runnable {
  @Override
  public void run() {
   synchronized (obj2) {
    System.out.println("Thread2 拿到 obj2 的锁");
    try {
     //停顿2秒的意义在于,让thread1线程拿到obj1的锁
     Thread.sleep(2000);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }

    synchronized (obj1) {
     System.out.println("Thread2 拿到 obj1 的锁");
    }
   }
  }
 }

}

产生死锁问题

使用Jps+Jstack查看进程死锁问题

使用jps命令查看运行中的进程

使用jstack命令查看进程java的具体线程信息

总结

死锁是多线程编程中常见的问题,对系统的性能和可用性有很大的影响。为了避免死锁的发生,我们可以遵循一些原则,如避免无谓的锁竞争按顺序获取锁使用定时锁使用并发工具类等。同时,我们还需要仔细设计资源申请顺序缩短持有锁的时间,并且可以引入一些避免死锁的算法来保证系统的正常运行。通过合理地选择和应用这些方法,我们可以有效地解决死锁问题,提高系统的性能和可靠性。

避免资源竞争

资源争夺解决方案

1、互斥锁:互斥锁是最常见的资源争夺解决方案之一,它确保同一时刻只有一个线程可以访问共享资源。Java提供了synchronized关键字和ReentrantLock类来实现互斥锁。使用互斥锁可以防止多个线程同时访问共享资源,从而避免数据竞争和一致性问题。

2、信号量:信号量是一种计数器,用于控制同时访问某个资源的线程数量。Java提供了Semaphore类来实现信号量。通过调用acquire()方法获取信号量,线程可以获得对资源的访问权限。使用信号量可以灵活地控制资源的访问权限,从而解决资源争夺问题。

3、读写锁:读写锁是一种特殊的锁,用于在读多写少的场景下提高并发性能。Java提供了ReentrantReadWriteLock类来实现读写锁。读写锁允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。通过使用读写锁,可以提高并发读取操作的性能,并减少争夺写操作的开销。

4、条件变量:条件变量是一种同步机制,用于在多线程环境下进行线程间通信和协调。Java提供了Condition接口和ReentrantLock类中的newCondition()方法来实现条件变量。通过使用条件变量,可以实现线程间的等待和通知机制,以解决复杂的线程协作和资源争夺问题。

5、同步集合:Java提供了许多同步集合类(如ConcurrentHashMap和ConcurrentLinkedQueue),用于在多线程环境下安全地访问和操作集合。这些同步集合类使用内部的同步机制来保证线程安全性和一致性,从而避免了手动同步和争夺资源的问题。

相关推荐
程序猿麦小七3 分钟前
基于springboot的景区网页设计与实现
java·spring boot·后端·旅游·景区
weisian15110 分钟前
认证鉴权框架SpringSecurity-2--重点组件和过滤器链篇
java·安全
蓝田~11 分钟前
SpringBoot-自定义注解,拦截器
java·spring boot·后端
.生产的驴14 分钟前
SpringCloud Gateway网关路由配置 接口统一 登录验证 权限校验 路由属性
java·spring boot·后端·spring·spring cloud·gateway·rabbitmq
v'sir28 分钟前
POI word转pdf乱码问题处理
java·spring boot·后端·pdf·word
提高记忆力36 分钟前
SpringBoot整合FreeMarker生成word表格文件
java·spring
JDS_DIJ37 分钟前
RabbitMQ
java·rabbitmq·java-rabbitmq
XiaoLeisj1 小时前
【JavaEE初阶 — 多线程】生产消费模型 & 阻塞队列
java·开发语言·java-ee
hxj..1 小时前
【设计模式】外观模式
java·设计模式·外观模式
冰逸.itbignyi1 小时前
SpringBoot之AOP 的使用
java·spring boot