java并发编程(九)活跃性危险

线程的活跃性危险主要包括死锁,饥饿和活锁。

死锁

  • 锁顺序死锁:两个线程试图以不同的顺序来获得相同的锁。
    比如一个线程先获取a锁后获取b锁,另一个线程先获取b锁后获取a锁。如果两个按照相同的顺序来请求锁(在相同时间请求a锁,在相同时间获取b锁),那么没问题,但是如果第一个线程获取了a锁的同时另一个线程获取了b锁,当两个线程分别获取第二个锁时会发现各自需要的锁都被对方持有了,也就发生了死锁。
    为了避免这种情况,可以使用**“加时赛”锁**,在获得两个对象的锁之前,首先获得这个加时赛锁,从而保证每次只有一个线程以未知的顺序获得这两个锁。
  • 在协作对象之间发生的死锁
    如果在持有锁的情况下调用某个外部方法,那么就需要警惕在协作对象之间发生死锁。
    如果在持有锁时调用某个外部方法,那么将出现活跃性问题,在这个外部方法中可能会获得其他锁(这可能会产生死锁),或者阻塞时间过长,导致其他线程无法及时获得当前被持有的锁。
  • 资源死锁
    线程饥饿死锁。如果某些任务需要等待其他任务的结果,那么这些任务往往是产生线程饥饿死锁的主要来源。

饥饿

当线程由于无法访问它所需要的资源而不能继续执行时,就发生了“饥饿”。更严重的情况会发生饥饿死锁。

在Thread API定义的线程优先级只是作为线程调度的参考。在Thread API中定义了10个优先级,JVM根据需要将它们映射到操作系统的调度优先级,这种映射时与特定平台(不同的操作系统)相关的。在某些操作系统中,如果优先级的数量少于10个,那么有多个Java优先级会被映射到同一个优先级。

要避免使用线程优先级,因为这会增加平台依赖性,并可能导致活跃性问题。在大多数并发应用程序中,都可以使用默认的线程优先级。

活锁

活锁是另一种形式的活跃性问题,该问题不会导致线程阻塞,但也不能继续执行。因为线程将不断重复执行相同的操作,而且总会失败。
比如某个线程操作失败后,放回队列。下一次执行又失败放回队列无限循环。
活锁通常时由过度的错误恢复代码造成的,因为它错误将不可修复的错误作为可修复的错误。
典型想rocketMq的消息重试机制,它设定了一个阀值,当重试次数到达阀值之后会抛弃这个消息。如果没有这个阀值,且消息一直消费错误,就产生了活锁。

通过线程转储信息来分析线程

java的线程转储可以被定义为JVM中在某一个给定的时刻运行的所有线程的快照。一个线程转储可能包含一个单独的线程或者多个线程。在多线程环境中,比如J2EE应用服务器,将会有许多线程和线程组。每一个线程都有它自己的调用堆栈,在一个给定时刻,表现为一个独立功能。线程转储将会提供JVM中所有线程的堆栈信息,对于特定的线程也会给出更多信息。

通过线程转储可以查看线程的运行状态,线程id,优先级等等一系列线程信息。

如何查看线程堆栈信息

  • 在linux服务器中 kill -3 pid (项目运行的jvm id)可以用ps aux|grep XXX命令查看
  • 在idea中可以直接在控制台中查看。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值