Java中的线程饥饿

时间:2020-01-09 10:35:07  来源:igfitidea点击:

在多线程环境中,对共享资源的访问是同步的,因此在任何给定时间只有一个线程可以进入同步上下文。从理论上讲,所有尝试获取对同步块或者方法的访问权限的线程都应定期获取一些CPU时间,但实际上这可能不会发生。如果线程无法获得对共享资源的常规访问并且无法取得进展,则在Java多线程中称为线程饥饿。

为什么发生线程饥饿

由于以下原因,可能会发生线程饥饿。

  • 假设一个对象具有一个同步方法,该方法需要大量的处理并且需要很长时间才能返回。如果一个线程频繁调用此方法,则也需要频繁同步访问同一对象的其他线程将经常被阻塞。
  • 具有较高优先级的线程被调度为首先运行,并占用所有CPU时间,从而导致具有最低优先级的线程继续等待调度运行。
  • 在同步上下文中,即使使用了wait方法来放弃对象的锁,等待线程之一也永远不会有机会进入同步上下文,因为其他一些线程被通知并计划运行。

线程饥饿Java示例

让我们尝试创建一种场景,使用上述原因,可能导致Java中的线程不足。

在代码中,线程t1被分配了最大线程优先级,并且它调用了三次同步方法。在为线程t2分配了最小线程优先级的情况下,该线程仅调用一次同步方法,但必须等待线程t1的所有三个调用才能执行同步方法。

class MyRunnable implements Runnable{
  ThreadDemo td;
  MyRunnable(ThreadDemo td){
    this.td = td;
  }
  @Override
  public void run() {
    td.testMethod();
    td.testMethod();
    td.testMethod();
  }	
}

class AnotherRunnable implements Runnable{
  ThreadDemo td;
  AnotherRunnable(ThreadDemo td){
    this.td = td;
  }
  @Override
  public void run() {
    td.testMethod();
  }	
}

public class ThreadDemo {
  public synchronized void testMethod(){
    try {
      System.out.println("Doing some heavy processing for thread " + 
        Thread.currentThread().getName());
      Thread.sleep(300);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
	
  public static void main(String[] args) {		
    ThreadDemo td = new ThreadDemo();
    Thread t1 = new Thread(new MyRunnable(td), "t1");
    Thread t2 = new Thread(new AnotherRunnable(td), "t2");

    t1.setPriority(Thread.MAX_PRIORITY);
    t2.setPriority(Thread.MIN_PRIORITY);

    t1.start();
    t2.start();
  }
}

输出:

Doing some heavy processing for thread t1
Doing some heavy processing for thread t1
Doing some heavy processing for thread t1
Doing some heavy processing for thread t2