Welcome to King of Concurrency, this is the 13th article in the series.

In the last article, we introduced several strategies for avoiding deadlocks. Although deadlocks are notorious, there are some equally important thread activity issues to be concerned about in concurrent programming besides deadlocks. They are less well known but highly destructive, and it is thread starvation and live locks that are covered in this article.

First, the emergence of hunger

Starvation refers to a situation in which greedy threads lock up resources and other threads starve to death while the wait is inconclusive.

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

(1) The thread is blocked indefinitely

When the thread that acquired the lock needs to perform an operation for an infinite amount of time (such as IO or an infinite loop), subsequent threads will block indefinitely, starving to death.

(2) Thread priority reduction does not gain 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 a specified thread when sending a notification. When multiple threads are in wait, some threads may remain unnotified and starve.

Hunger and justice

To get a sense of thread hunger, we created the following code.

Create nezha, Lanling King and other four hero players, they fight wild in a competitive way, killing wild monsters can gain economic benefits.

public class StarvationExample {

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

    String[] players = {
      "Which zha"."The King of Lanling"."Armor"."Dian wei"
    };
    for (String player: players) {
      Thread playerThread = new Thread(new Runnable() {
        public void run(a) { wildMonster.killWildMonster(); }}); playerThread.setName(player); playerThread.start(); }}}Copy the code
 public class WildMonster {
   public synchronized void killWildMonster(a) {
     while (true) {
       String playerName = Thread.currentThread().getName();
       System.out.println(playerName + "Take the wild one!);
       try {
         Thread.sleep(500);
       } catch (InterruptedException e) {
         System.out.println("Wild break"); }}}}Copy the code

The running results are as follows:

Nezha conquers the wild monster! Nezha conquers the wild monster! Nezha conquers the wild monster! Nezha conquers the wild monster! Nezha conquers the wild monster! Nezha conquers the wild monster! Nezha conquers the wild monster! Nezha conquers the wild monster! Nezha conquers the wild monster! Nezha conquers the wild monster! Nezha conquers the wild monster! Process finished with exit code 130 (interrupted by signal 2: SIGINT)Copy the code

As can be seen from the result, in several threads running, only Ne Zha can defeat the monster, while the other heroes are helpless and waiting to starve to death. Why did this happen?

Looking closely at the code in the WildMonster class, the problem lies in the killWildMonster synchronization method. Once a hero enters the method, the object lock is held forever, and other threads are blocked from entering.

Of course, the solution is simple: just break the monopoly. For example, if we change thread. sleep to wait in the code below, the problem will be solved.

 public static class WildMonster {
   public synchronized void killWildMonster(a) {
     while (true) {
       String playerName = Thread.currentThread().getName();
       System.out.println(playerName + "Take the wild one!);
       try {
         wait(500);
       } catch (InterruptedException e) {
         System.out.println("Wild break"); }}}}Copy the code

The running results are as follows:

Nezha conquers the wild monster! Armored wild monster! The King of Lanling has captured the wild monster! Dian Wei captured the wild monster! The King of Lanling has captured the wild monster! Dian Wei captured the wild monster! Process finished with exit code 130 (interrupted by signal 2: SIGINT)Copy the code

As can be seen from the result, all four heroes got the chance to play wild, which realized fairness to a certain extent. (Note: Wait releases locks, but sleep does not, so if you don’t understand this, check out the Bronze series.)

How to make fair competition between threads is an important topic in threading issues. Although we can’t guarantee 100% fairness, we can still increase fairness between threads by designing certain data structures and using appropriate utility classes.

With respect to fairness between threads, it’s important to understand its existence and importance in this article, and we’ll cover concurrency utility classes in a future article on how to elegantly address them.

Three, the trouble of live lock

You may not be as familiar with live locks as deadlocks. However, live locks are as bad as deadlocks. As a result, live locks and deadlocks are disastrous, causing the application to fail to provide proper service.

LiveLock is when two threads are busy responding to each other’s requests, but not doing their own work. They repeat specific code over and over and accomplish nothing.

Unlike deadlocks, live locks do not cause threads to block, but they spin in circles, so their effect is similar to that of deadlocks. Programs go into an infinite loop and cannot proceed.

If you can’t intuitively understand what a live lock is, you’ve probably encountered the following situation while walking. Two people walk towards each other, out of politeness two people give way to each other, and the result is still two people can not pass. Live lock, that’s the same thing.

summary

That’s all about thread hunger and live locking. In this article, we introduce the causes of thread hunger. There is no 100% solution to thread hunger, but you can level the playing field as much as possible. We haven’t listed some of the tool classes for thread fairness in this article, because I think understanding the problem is more important than the solution. If there is no understanding of the problem, the scheme will also appear to know what it is and not know why. In addition, while live locks are not as well known as deadlocks, a proper understanding of live locks is still necessary as part of the concurrent knowledge system.

This is the end of the text, congratulations you on a star ✨

The test of the master

  • Write code to set priorities for different threads, experience thread hunger, and propose solutions.

Extensive reading and references

  • Motion picture reference
  • “King concurrent class” outline and update progress overview

About the author

Follow technology 8:30am for updates. Deliver high-quality technical articles, push author quality original articles at 8:30 in the morning, push industry in-depth good articles at 20:30 at night.

If this article has been helpful to you, please like it, follow it, and monitor it as we go from bronze to King.