Gold 3: rain and dew - don't let your thread starve to death in the competition

Posted by php_novice2007 on Sun, 30 Jan 2022 00:21:20 +0100

Welcome to< Concurrent King class >, this article is the 13th in this series.

In the last article, we introduced several strategies to avoid deadlock. Although deadlock is notorious, in concurrent programming, in addition to deadlock, there are some equally important thread activity issues that deserve attention. Their popularity is not high, but they are very destructive. This paper will introduce the thread hunger and livelock problems.

1, The emergence of hunger

The so-called thread Starvation refers to that in the multi-threaded resource competition, greedy threads always lock resources and do not release them, while other threads are always in a waiting state. However, this waiting has no result, and they will starve to death alive.

The greed of monopolists is one of the causes of hunger. Generally speaking, hunger is generally caused by the following three reasons:

(1) Thread is infinitely blocked

When the thread that obtains the lock needs to perform an operation for an infinite time (such as IO or infinite loop), the subsequent thread will be infinitely blocked, resulting in starvation.

(2) Thread priority reduction does not get CPU time

When multiple competing threads are prioritized, the higher the priority, the more CPU time the thread is given. In some extreme cases, low priority threads may never be granted sufficient CPU time, resulting in starvation.

(3) Threads are always waiting for resources

In the bronze series, we said that notify cannot wake up the specified thread when sending a notification. When multiple threads are wait ing, some threads may not be notified and starve.

2, Hunger and equity

In order to intuitively experience the hunger of threads, we created the following code.

Create four hero players such as Nezha and Lanling king. They fight wild in a competitive way. Killing wild monsters can obtain economic benefits.

public class StarvationExample {

  public static void main(String[] args) {
    final WildMonster wildMonster = new WildMonster();

    String[] players = {
      "nezha",
      "King Lanling",
      "Armor",
      "Dianwei"
    };
    for (String player: players) {
      Thread playerThread = new Thread(new Runnable() {
        public void run() {
          wildMonster.killWildMonster();
        }
      });
      playerThread.setName(player);
      playerThread.start();
    }
  }
}

 public class WildMonster {
   public synchronized void killWildMonster() {
     while (true) {
       String playerName = Thread.currentThread().getName();
       System.out.println(playerName + "Capture the wild monster!");
       try {
         Thread.sleep(500);
       } catch (InterruptedException e) {
         System.out.println("Open field interrupt");
       }
     }
   }
 }

The operation results are as follows:

Nezha captured the wild monster!
Nezha captured the wild monster!
Nezha captured the wild monster!
Nezha captured the wild monster!
Nezha captured the wild monster!
Nezha captured the wild monster!
Nezha captured the wild monster!
Nezha captured the wild monster!
Nezha captured the wild monster!
Nezha captured the wild monster!
Nezha captured the wild monster!

Process finished with exit code 130 (interrupted by signal 2: SIGINT)

It can be seen from the results that in the operation of several threads, only Nezha can capture the wild monster, and other heroes are helpless waiting to starve to death. Why did this happen?

Take a closer look at the code in the WildMonster class. The problem lies in the killwidmonster synchronization method. Once a hero enters this method, it will always hold the object lock, and other threads will be blocked and cannot enter again.

Of course, the solution is also very simple, as long as you break the monopoly. For example, we put thread. In the following code If sleep is changed to wait, the problem will be solved.

 public static class WildMonster {
   public synchronized void killWildMonster() {
     while (true) {
       String playerName = Thread.currentThread().getName();
       System.out.println(playerName + "Capture the wild monster!");
       try {
         wait(500);
       } catch (InterruptedException e) {
         System.out.println("Open field interrupt");
       }
     }
   }
 }

The operation results are as follows:

Nezha captured the wild monster!
Armor captures wild monsters!
The king of Lanling captured the wild monster!
Dianwei captured the wild monster!
The king of Lanling captured the wild monster!
Dianwei captured the wild monster!

Process finished with exit code 130 (interrupted by signal 2: SIGINT)

It can be seen from the results that the four heroes have obtained the opportunity to fight in the wild and achieved fairness to a certain extent. (Note: wait will release the lock, but sleep will not. If you don't understand this, you can check the bronze series.)

How to make the threads compete fairly is an important topic in the thread problem. Although we cannot guarantee 100% fairness, we still need to design certain data structures and use corresponding tool classes to increase the fairness between threads.

As for the fairness between threads, it is important to understand its existence and importance in this article. As for how to solve it gracefully, we will introduce the related concurrency tool classes in subsequent articles.

3, Live lock trouble

Compared with deadlocks, you may not be so familiar with livelocks. However, livelocks have no less negative impact than deadlocks. As a result, both livelocks and deadlocks are catastrophic, which will cause the application to fail to provide normal service capabilities.

The so-called LiveLock means that two threads are busy responding to each other's requests, but they don't do their own business. They keep repeating specific code, but accomplish nothing.

Unlike deadlocks, livelocks do not cause threads to enter the blocking state, but they will spin in place. Therefore, the impact is similar to deadlocks. The program will enter a wireless dead loop and cannot continue.

If you can't intuitively understand what a livelock is, I believe you must have encountered the following situations when walking. They walked opposite each other. Out of politeness, they gave way to each other and let each other go. As a result, they were still unable to pass. Livelock, that's what it means.

Summary

The above is all about thread hunger and livelock. In this article, we introduce the causes of thread starvation. There is no 100% solution to thread hunger, but fair competition can be achieved as far as possible. We didn't list some tool classes of thread fairness in this article, because I think the understanding of the problem is more important than the solution. If there is no understanding of the problem, the scheme will be known but not known when it is implemented. In addition, although livelocks are not as well-known as deadlocks, a proper understanding of livelocks is still very necessary. It is a part of concurrent knowledge system.

That's the end of the text. Congratulations on another star ✨

Master's trial

  • Write code to set the priority of different threads, experience thread hunger and give solutions.

Extended reading and reference materials

About the author

Pay attention to the official account. Mediocre technical jokes ], get timely article updates. Record the technical stories of ordinary people, share high-quality (as far as possible) technical articles, and occasionally talk about life and ideals. No selling anxiety, no title party.

If this article is helpful to you, welcome to praise, pay attention and supervise. Let's go from bronze to King.

Topics: Java Interview Multithreading Concurrent Programming