返回顶部
首页 > 资讯 > 精选 >Java中导致饥饿的原因是什么
  • 372
分享到

Java中导致饥饿的原因是什么

java 2023-05-30 22:05:02 372人浏览 八月长安
摘要

小编给大家分享一下Java中导致饥饿的原因是什么,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!如果一个线程因为CPU时间全部被其他线程抢走而得不到CPU运行时间,

小编给大家分享一下Java中导致饥饿的原因是什么,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

如果一个线程因为CPU时间全部被其他线程抢走而得不到CPU运行时间,这种状态被称之为“饥饿”。而该线程被“饥饿致死”正是因为它得不到CPU运行时间的机会。解决饥饿的方案被称之为“公平性” – 即所有线程均能公平地获得运行机会。

 下面是本文讨论的主题:

Java中导致饥饿的原因

在Java中,下面三个常见的原因会导致线程饥饿:

高优先级线程吞噬所有的低优先级线程的CPU时间。
线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总是能在它之前持续地对该同步块进行访问。
线程在等待一个本身(在其上调用wait())也处于永久等待完成的对象,因为其他线程总是被持续地获得唤醒。

高优先级线程吞噬所有的低优先级线程的CPU时间

你能为每个线程设置独自的线程优先级,优先级越高的线程获得的CPU时间越多,线程优先级值设置在1到10之间,而这些优先级值所表示行为的准确解释则依赖于你的应用运行平台。对大多数应用来说,你最好是不要改变其优先级值。

线程被永久堵塞在一个等待进入同步块的状态

Java的同步代码区也是一个导致饥饿的因素。Java的同步代码区对哪个线程允许进入的次序没有任何保障。这就意味着理论上存在一个试图进入该同步区的线程处于被永久堵塞的风险,因为其他线程总是能持续地先于它获得访问,这即是“饥饿”问题,而一个线程被“饥饿致死”正是因为它得不到CPU运行时间的机会。

线程在等待一个本身(在其上调用wait())也处于永久等待完成的对象

如果多个线程处在wait()方法执行上,而对其调用notify()不会保证哪一个线程会获得唤醒,任何线程都有可能处于继续等待的状态。因此存在这样一个风险:一个等待线程从来得不到唤醒,因为其他等待线程总是能被获得唤醒。

下面分享一个线程饥饿死的例子,代码:

public class ThreadDeadlock {ExecutorService exec = Executors.newSingleThreadScheduledExecutor();//  ExecutorService exec = Executors.newCachedThreadPool();  //如果添加给线程池中添加足够多的线程,就可以让所有任务都执行,避免饥饿死锁。 class RenderPageTask implements Callable<String>{@Override     public String call() throws Exception {Future<String> header,footer;header = exec.submit(new Callable<String>(){@Override         public String call() throws Exception {System.out.println("加载页眉");Thread.sleep(2*1000);return "页眉";}});footer = exec.submit(new Callable<String>(){@Override         public String call() throws Exception {System.out.println("加载页脚");Thread.sleep(3*1000);return "页脚";}});System.out.println("渲染页面主体");return header.get() + footer.get();}}public static void main(String[] args) throws InterruptedException, ExecutionException {ThreadDeadlock td = new ThreadDeadlock();Future<String> futre = td.exec.submit(td.new RenderPageTask());String result = futre.get();System.out.println("执行结果为:" + result);}}

在Java中实现公平性

虽Java不可能实现100%的公平性,我们依然可以通过同步结构在线程间实现公平性的提高。

首先来学习一段简单的同步态代码:

public class Synchronizer{  public synchronized void doSynchronized(){  //do a lot of work which takes a long time  }}

如果有一个以上的线程调用doSynchronized()方法,在第一个获得访问的线程未完成前,其他线程将一直处于阻塞状态,而且在这种多线程被阻塞的场景下,接下来将是哪个线程获得访问是没有保障的。

使用锁方式替代同步块

为了提高等待线程的公平性,我们使用锁方式来替代同步块。

public class Synchronizer{  Lock lock = new Lock();  public void doSynchronized() throws InterruptedException{    this.lock.lock();    //critical section, do a lot of work which takes a long time    this.lock.unlock();  }}

注意到doSynchronized()不再声明为synchronized,而是用lock.lock()和lock.unlock()来替代。

下面是用Lock类做的一个实现:

public class Lock{private Boolean isLocked   = false;private Thread lockingThread = null;public synchronized void lock() throws InterruptedException{while(isLocked){wait();}isLocked = true;lockingThread = Thread.currentThread();}public synchronized void unlock(){if(this.lockingThread != Thread.currentThread()){throw new IllegalMonitorStateException(       "Calling thread has not locked this lock");}isLocked = false;lockingThread = null;notify();}}

注意到上面对Lock的实现,如果存在多线程并发访问lock(),这些线程将阻塞在对lock()方法的访问上。另外,如果锁已经锁上(校对注:这里指的是isLocked等于true时),这些线程将阻塞在while(isLocked)循环的wait()调用里面。要记住的是,当线程正在等待进入lock() 时,可以调用wait()释放其锁实例对应的同步锁,使得其他多个线程可以进入lock()方法,并调用wait()方法。

这回看下doSynchronized(),你会注意到在lock()和unlock()之间的注释:在这两个调用之间的代码将运行很长一段时间。进一步设想,这段代码将长时间运行,和进入lock()并调用wait()来比较的话。这意味着大部分时间用在等待进入锁和进入临界区的过程是用在wait()的等待中,而不是被阻塞在试图进入lock()方法中。

在早些时候提到过,同步块不会对等待进入的多个线程谁能获得访问做任何保障,同样当调用notify()时,wait()也不会做保障一定能唤醒线程(至于为什么,请看线程通信)。因此这个版本的Lock类和doSynchronized()那个版本就保障公平性而言,没有任何区别。

但我们能改变这种情况。当前的Lock类版本调用自己的wait()方法,如果每个线程在不同的对象上调用wait(),那么只有一个线程会在该对象上调用wait(),Lock类可以决定哪个对象能对其调用notify(),因此能做到有效的选择唤醒哪个线程。

公平锁

下面来讲述将上面Lock类转变为公平锁FairLock。你会注意到新的实现和之前的Lock类中的同步和wait()/notify()稍有不同。
准确地说如何从之前的Lock类做到公平锁的设计是一个渐进设计的过程,每一步都是在解决上一步的问题而前进的:Nested Monitor Lockout, Slipped Conditions和Missed Signals。这些本身的讨论虽已超出本文的范围,但其中每一步的内容都将会专题进行讨论。重要的是,每一个调用lock()的线程都会进入一个队列,当解锁后,只有队列里的第一个线程被允许锁住Farlock实例,所有其它的线程都将处于等待状态,直到他们处于队列头部。

public class FairLock {private Boolean      isLocked    = false;private Thread      lockingThread = null;private List<QueueObject> waitingThreads =      new ArrayList<QueueObject>();public void lock() throws InterruptedException{QueueObject queueObject      = new QueueObject();Boolean   isLockedForThisThread = true;synchronized(this){waitingThreads.add(queueObject);}while(isLockedForThisThread){synchronized(this){isLockedForThisThread =      isLocked || waitingThreads.get(0) != queueObject;if(!isLockedForThisThread){isLocked = true;waitingThreads.remove(queueObject);lockingThread = Thread.currentThread();return;}}try{queueObject.doWait();}catch(InterruptedException e){synchronized(this) {waitingThreads.remove(queueObject);}throw e;}}}public synchronized void unlock(){if(this.lockingThread != Thread.currentThread()){throw new IllegalMonitorStateException(    "Calling thread has not locked this lock");}isLocked   = false;lockingThread = null;if(waitingThreads.size() > 0){waitingThreads.get(0).doNotify();}}}
public class QueueObject {private Boolean isNotified = false;public synchronized void doWait() throws InterruptedException {while(!isNotified){this.wait();}this.isNotified = false;}public synchronized void doNotify() {this.isNotified = true;this.notify();}public Boolean equals(Object o) {return this == o;}}

首先注意到lock()方法不在声明为synchronized,取而代之的是对必需同步的代码,在synchronized中进行嵌套。

FairLock新创建了一个QueueObject的实例,并对每个调用lock()的线程进行入队列。调用unlock()的线程将从队列头部获取QueueObject,并对其调用doNotify(),以唤醒在该对象上等待的线程。通过这种方式,在同一时间仅有一个等待线程获得唤醒,而不是所有的等待线程。这也是实现FairLock公平性的核心所在。

请注意,在同一个同步块中,锁状态依然被检查和设置,以避免出现滑漏条件。

还需注意到,QueueObject实际是一个semaphore。doWait()和doNotify()方法在QueueObject中保存着信号。这样做以避免一个线程在调用queueObject.doWait()之前被另一个调用unlock()并随之调用queueObject.doNotify()的线程重入,从而导致信号丢失。queueObject.doWait()调用放置在synchronized(this)块之外,以避免被monitor嵌套锁死,所以另外的线程可以解锁,只要当没有线程在lock方法的synchronized(this)块中执行即可。

最后,注意到queueObject.doWait()在try – catch块中是怎样调用的。在InterruptedException抛出的情况下,线程得以离开lock(),并需让它从队列中移除。

性能考虑

如果比较Lock和FairLock类,你会注意到在FairLock类中lock()和unlock()还有更多需要深入的地方。这些额外的代码会导致FairLock的同步机制实现比Lock要稍微慢些。究竟存在多少影响,还依赖于应用在FairLock临界区执行的时长。执行时长越大,FairLock带来的负担影响就越小,当然这也和代码执行的频繁度相关。

以上是“Java中导致饥饿的原因是什么”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网精选频道!

--结束END--

本文标题: Java中导致饥饿的原因是什么

本文链接: https://lsjlt.com/news/221852.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Java中导致饥饿的原因是什么
    小编给大家分享一下Java中导致饥饿的原因是什么,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!如果一个线程因为CPU时间全部被其他线程抢走而得不到CPU运行时间,...
    99+
    2023-05-30
    java
  • Java中ThreadLocal导致内存OOM的原因是什么
    本篇内容介绍了“Java中ThreadLocal导致内存OOM的原因是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!原因分析Thread...
    99+
    2023-06-30
  • Java并发中死锁、活锁和饥饿是什么意思
    解答 死锁是指两个或者两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,他们将无法推进下去。 如果线程的智力不够, 且都秉承着“谦让”的原则,...
    99+
    2024-04-02
  • React中的任务饥饿行为是什么意思
    本篇内容主要讲解“React中的任务饥饿行为是什么意思”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“React中的任务饥饿行为是什么意思”吧!本文是在React...
    99+
    2024-04-02
  • 导致电脑老是蓝屏的原因是什么
    这篇文章主要介绍导致电脑老是蓝屏的原因是什么,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!假如经常性蓝屏,后来重装系统后还是蓝屏,证明是硬件出现问题,一般都是查看内存条或者显卡。可以直接断电,打开机箱,取出内存条后用...
    99+
    2023-06-27
  • 导致SELinux警告产生的原因是什么
    本篇内容主要讲解“导致SELinux警告产生的原因是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“导致SELinux警告产生的原因是什么”吧!原因一:出现标注错误 SELinux 的核心概念...
    99+
    2023-06-13
  • 导致服务器丢包的原因是什么
    导致服务器丢包的原因可能有以下几点: 网络拥堵:当网络流量过大导致网络拥堵时,服务器可能无法及时处理所有数据包,从而导致丢包。 ...
    99+
    2024-04-26
    服务器
  • 外链导致网站降权的原因是什么
    本篇文章给大家分享的是有关外链导致网站降权的原因是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。一、外链建设速度过快外链添加速度过快,会让...
    99+
    2024-04-02
  • 云服务器很卡是什么原因导致的
    造成云服务器卡顿的原因有很多,主要包括以下几个方面: 硬件故障:当云服务器出现硬件故障时,服务器会出现性能下降、卡顿等问题,这会导致用户无法正常访问云服务器提供的服务。 软件问题:云服务器是基于虚拟化技术构建的,需要运行大量的操作系统、...
    99+
    2023-10-27
    服务器
  • Java中ThreadLocal 导致内存 OOM 的原因分析
    目录原因分析正确的使用方式原因分析 ThreadLocal 导致内存 OOM 的原因是什么? ThreadLocal 底层通过 ThreadLocalMap 存储数据 源码如下:&n...
    99+
    2024-04-02
  • 导致服务器丢包严重的原因是什么
    服务器丢包严重的原因可能包括: 网络拥堵:当网络流量过大超过服务器处理能力时,数据包很容易被丢弃。 网络故障:网络设备故障或...
    99+
    2024-05-06
    服务器
  • 连接云服务器失败是什么原因导致的
    如果您的云服务器连接到云平台时发生了失败,通常会出现以下几种原因: 网络连接问题:云平台会通过各种网络接口和连接方式连接用户的云服务器,当连接问题导致云服务器无法正确识别您的云平台时,就会出现连接失败的情况。 配置错误:在云平台上启用了...
    99+
    2023-10-27
    服务器
  • 导致香港服务器IP被封的原因是什么
    香港服务器IP被封的原因可能有很多种,其中一些可能包括: 违反国家法律规定:如果服务器上托管的内容违反了香港或中国的法律规定,可...
    99+
    2024-04-24
    香港服务器 服务器
  • 浏览器中导致SSL证书不被信任的原因是什么
    SSL证书不被信任的原因可能有以下几种: 证书签发机构不受浏览器信任:浏览器内置一些受信任的证书颁发机构(CA),如果网站的SS...
    99+
    2024-05-09
    SSL证书
  • 香港服务器内存不足是什么原因导致的
    导致香港服务器内存不足的原因:1、香港服务器应用程序池没有及时释放内存导致;2、香港服务器本身内存资源不足,无法满足当前业务需求导致;3、香港服务器上运行的程序过多,消耗内存资源过大导致;4、香港服务器正在遭受网络恶意攻击导致服务器资源被占...
    99+
    2024-04-02
  • 电脑静电导致显示器黑屏的原因是什么
    本篇内容介绍了“电脑静电导致显示器黑屏的原因是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!电脑静电导致显示器黑屏吗:答:静电和显示器黑...
    99+
    2023-07-01
  • 线程崩溃不会导致JVM崩溃的原因是什么
    本文小编为大家详细介绍“线程崩溃不会导致JVM崩溃的原因是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“线程崩溃不会导致JVM崩溃的原因是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。线程崩溃,进程一定...
    99+
    2023-07-02
  • 轻量应用服务器缺点是什么原因导致的
    轻量应用服务器缺点主要是以下几个方面: 性能:轻量应用服务器在处理较小的请求时可能会比常规应用服务器慢,尤其是对于轻量应用服务器来说。这可能是由于轻量应用服务器在处理相同数量的请求时需要更少的内存和CPU,从而导致性能下降。 响应延迟:...
    99+
    2023-10-26
    缺点 服务器
  • 什么原因导致香港服务器网络中断
    导致香港服务器网络中断的原因有:1、香港服务器服务商的网络异常导致;2、香港服务器的硬件设备出现故障导致;3、香港服务器的机房出现电源故障导致;4、香港服务器正在遭受攻击,机房运维人员采用了紧急关机的方式规避,从而导致香港服务器网络中断。具...
    99+
    2024-04-02
  • 什么原因导致服务器VPS的IP被封
    导致服务器VPS IP被封的原因有:1、服务器运营内容出现问题,导致IP被封,严重时会导致服务器被关闭暂停使用;2、所使用的IP之前出现问题,如做一些不合法本地政策不允许的项目等;3、IP所属的IP段出现问题,导致IP被封;4、所使用的IP...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作