Java multi-threaded programming-Wait timeout control

Foreword:

This article is based on my personal understanding of Chapter 5 of "Java Multi-Threaded Programming Practical Guide". Because Chapter 5 contains a lot of content, it is divided into I have written many articles. The source code is excerpted from the author's source code, and my own understanding will be added to the source code.

The reading notes are currently being updated by the author as follows, "Java Multithreaded Programming Practical Guide - Core Chapter", "How Tomcat Works", and Go to "spring source code" for interpretation.

Wait timeout:

Object.wait(long) allows us to set a timeout (in milliseconds). If the suspended waiting thread is not awakened by other threads within this time, the Java virtual machine will automatically wake up the thread.

However, Object.wait(long) neither returns a value nor throws a specific exception, so as to distinguish whether its return is due to other threads notifying the current thread or due to waiting timeout. Therefore, we need some additional processing when using Object.wait(long).

Example:



import java.util.Random;

public class TimeoutWaitExample {
  private static final Object lock = new Object();
  private static boolean ready = false;
  protected static final Random random = new Random();

  public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread() {
      @Override
      public void run() {
        for (;;) {
          synchronized (lock) {
            ready = random.nextInt(100) < 5 ? true : false;
            if (ready) {
              lock.notify();
            }
          }
          // 使当前线程暂停一段(随机)时间
          Tools.randomPause(500);
        }// for循环结束
      }
    };
    t.setDaemon(true);
    t.start();
    waiter(1000);
  }

  public static void waiter(final long timeOut) throws InterruptedException {
    if (timeOut < 0) {
      throw new IllegalArgumentException();
    }

    long start = System.currentTimeMillis();
    long waitTime;
    long now;
    synchronized (lock) {
      while (!ready) {
        now = System.currentTimeMillis();
        // 计算剩余等待时间
        waitTime = timeOut - (now - start);
        Debug.info("Remaining time to wait:%sms", waitTime);
        if (waitTime <= 0) {
          // 等待超时退出
          break;
        }
        lock.wait(waitTime);
      }// while循环结束

      if (ready) {
        // 执行目标动作
        guardedAction();
      } else {
        // 等待超时,保护条件未成立
        Debug.error("Wait timed out,unable to execution target action!");
      }
    }// 同步块结束
  }

  private static void guardedAction() {
    Debug.info("Take some action.");
    // ...
  }
}

Basic process explanation: 

1. During initialization, construct a static object Object

2. Created a notification thread Thread t, which is a daemon thread, and loops through the for value, random.nextInt. The probability of getting greater than 5 within 100 is relatively high, so the high probability bit is false and the small probability bit is true.

3. So the main thread is in the while loop of ready=falise and stops at lock.wait. The main thread enters the waiting state. The waiting time needs to deduct part of the code to the wait statement time.

4. Finally, when the time reaches the waiting time, the ready condition is judged. If it is true, the corresponding action will be executed. If it is not true, the corresponding action cannot be executed. So there are two results from running the above code, both of which are possible.

Other codes:

import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Debug {
    private static ThreadLocal<SimpleDateFormat> sdfWrapper = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        }

    };

    enum Label {
        INFO("INFO"),
        ERR("ERROR");
        String name;

        Label(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

    // public static void info(String message) {
    // printf(Label.INFO, "%s", message);
    // }

    public static void info(String format, Object... args) {
        printf(Label.INFO, format, args);
    }

    public static void info(boolean message) {
        info("%s", message);
    }

    public static void info(int message) {
        info("%d", message);
    }

    public static void error(String message, Object... args) {
        printf(Label.ERR, message, args);
    }

    public static void printf(Label label, String format, Object... args) {
        SimpleDateFormat sdf = sdfWrapper.get();
        @SuppressWarnings("resource")
        final PrintStream ps = label == Label.INFO ? System.out : System.err;
        ps.printf('[' + sdf.format(new Date()) + "][" + label.getName()
                + "]["
                + Thread.currentThread().getName() + "]:" + format + " %n", args);
    }
}
import sun.misc.Unsafe;

import java.io.*;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

public final class Tools {
    private static final Random rnd = new Random();
    private static final Logger LOGGER = Logger.getAnonymousLogger();

    public static void startAndWaitTerminated(Thread... threads)
            throws InterruptedException {
        if (null == threads) {
            throw new IllegalArgumentException("threads is null!");
        }
        for (Thread t : threads) {
            t.start();
        }
        for (Thread t : threads) {
            t.join();
        }
    }

    public static void startThread(Thread... threads) {
        if (null == threads) {
            throw new IllegalArgumentException("threads is null!");
        }
        for (Thread t : threads) {
            t.start();
        }
    }

    public static void startAndWaitTerminated(Iterable<Thread> threads)
            throws InterruptedException {
        if (null == threads) {
            throw new IllegalArgumentException("threads is null!");
        }
        for (Thread t : threads) {
            t.start();
        }
        for (Thread t : threads) {
            t.join();
        }
    }

    public static void randomPause(int maxPauseTime) {
        int sleepTime = rnd.nextInt(maxPauseTime);
        try {
            Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    public static void randomPause(int maxPauseTime, int minPauseTime) {
        int sleepTime = maxPauseTime == minPauseTime ? minPauseTime : rnd
                .nextInt(maxPauseTime - minPauseTime) + minPauseTime;
        try {
            Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    public static Unsafe getUnsafe() {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            ((Field) f).setAccessible(true);
            return (Unsafe) f.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void silentClose(Closeable... closeable) {
        if (null == closeable) {
            return;
        }
        for (Closeable c : closeable) {
            if (null == c) {
                continue;
            }
            try {
                c.close();
            } catch (Exception ignored) {
            }
        }
    }

    public static void split(String str, String[] result, char delimeter) {
        int partsCount = result.length;
        int posOfDelimeter;
        int fromIndex = 0;
        String recordField;
        int i = 0;
        while (i < partsCount) {
            posOfDelimeter = str.indexOf(delimeter, fromIndex);
            if (-1 == posOfDelimeter) {
                recordField = str.substring(fromIndex);
                result[i] = recordField;
                break;
            }
            recordField = str.substring(fromIndex, posOfDelimeter);
            result[i] = recordField;
            i++;
            fromIndex = posOfDelimeter + 1;
        }
    }

    public static void log(String message) {
        LOGGER.log(Level.INFO, message);
    }

    public static String md5sum(final InputStream in) throws NoSuchAlgorithmException, IOException {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] buf = new byte[1024];
        try (DigestInputStream dis = new DigestInputStream(in, md)) {
            while (-1 != dis.read(buf))
                ;
        }
        byte[] digest = md.digest();
        BigInteger bigInt = new BigInteger(1, digest);
        String checkSum = bigInt.toString(16);

        while (checkSum.length() < 32) {
            checkSum = "0" + checkSum;
        }
        return checkSum;
    }

    public static String md5sum(final File file) throws NoSuchAlgorithmException, IOException {
        return md5sum(new BufferedInputStream(new FileInputStream(file)));
    }

    public static String md5sum(String str) throws NoSuchAlgorithmException, IOException {
        ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes("UTF-8"));
        return md5sum(in);
    }

    public static void delayedAction(String prompt, Runnable action, int delay/* seconds */) {
        Debug.info("%s in %d seconds.", prompt, delay);
        try {
            Thread.sleep(delay * 1000);
        } catch (InterruptedException ignored) {
        }
        action.run();
    }

    public static Object newInstanceOf(String className) throws InstantiationException,
            IllegalAccessException, ClassNotFoundException {
        return Class.forName(className).newInstance();
    }

}

references:

"Java Multi-Threaded Programming Practical Guide-Core"

Guess you like

Origin blog.csdn.net/u012895183/article/details/133174786