Means of communication

To achieve collaboration between multiple threads, such as: thread execution sequence, obtain the results of a thread execution, and so on. The communication between threads is divided into the following four categories:

  • File sharing
  • A network share
  • Shared variables
  • The THREAD coordination API provided by the JDK
    • Suspend /resume, wait/notify, and park/unpark

File sharing

public class MainTest {

  public static void main(String[] args) {
    Thread 1 - writes data
    new Thread(() -> {
      try {
        while (true) {
          Files.write(Paths.get("test.log"),
          content = "Current time" + String.valueOf(System.currentTimeMillis()));
          Thread.sleep(1000L); }}catch (Exception e) {
        e.printStackTrace();
      }
    }).start();

    Thread 2 - reads data
    new Thread(() -> {
      try {
        while (true) {
          Thread.sleep(1000L);
          byte[] allBytes = Files.readAllBytes(Paths.get("test.log"));
          System.out.println(newString(allBytes)); }}catch(Exception e) { e.printStackTrace(); } }).start(); }}Copy the code

Variable Shared

public class MainTest {
  // Share variables
  public static String content = "Empty";

  public static void main(String[] args) {
    Thread 1 - writes data
    new Thread(() -> {
      try {
        while (true) {
          content = "Current time" + String.valueOf(System.currentTimeMillis());
          Thread.sleep(1000L); }}catch (Exception e) {
        e.printStackTrace();
      }
    }).start();

    Thread 2 - reads data
    new Thread(() -> {
      try {
        while (true) {
          Thread.sleep(1000L); System.out.println(content); }}catch(Exception e) { e.printStackTrace(); } }).start(); }}Copy the code

A network share

Thread collaboration -JDK API

The JDK provides API support for scenarios that require multiple threads to collaborate on a task.

The typical scenario for multithreaded collaboration is the producer-consumer model. (Thread blocking, thread wake up)

Example: thread 1 goes to buy a bun, no bun, no longer execute. Thread 2 produces the bun and tells thread -1 to continue.

API- Deprecated suspend and resume

Call suspend to suspend the target thread. Resume is used to resume the thread.

/** steamed stuffed bun shop */
public static Object baozidian = null;

Suspend /resume */
public void suspendResumeTest(a) throws Exception {
    // Start the thread
    Thread consumerThread = new Thread(() -> {
        if (baozidian == null) { // If there is no bun, enter the wait
            System.out.println("1. Enter the wait");
            Thread.currentThread().suspend();
        }
        System.out.println("2. Buy the steamed stuffed bun and go home");
    });
    consumerThread.start();
    // After 3 seconds, produce one bun
    Thread.sleep(3000L);
    baozidian = new Object();
    consumerThread.resume();
    System.out.println("3. Inform consumers");
}
Copy the code

The main reason for this deprecation was that it was easy to write code for undead locks. Instead, use wait/notify and park/unpark

Suspend and Resume deadlock example

1. Use in synchronous code

	/** deadlocked suspend/resume. Suspend does not release locks like wait, so it is easy to write deadlock code */
	public void suspendResumeDeadLockTest(a) throws Exception {
		// Start the thread
		Thread consumerThread = new Thread(() -> {
			if (baozidian == null) { // If there is no bun, enter the wait
				System.out.println("1. Enter the wait");
				// The current thread takes the lock and suspends it
				synchronized (this) {
					Thread.currentThread().suspend();
				}
			}
			System.out.println("2. Buy the steamed stuffed bun and go home");
		});
		consumerThread.start();
		// After 3 seconds, produce one bun
		Thread.sleep(3000L);
		baozidian = new Object();
		// Restore the consumerThread after securing the lock
		synchronized (this) {
			consumerThread.resume();
		}
		System.out.println("3. Inform consumers");
	}
Copy the code

2. Suspend rather than resume

Suspend /resume */
	public void suspendResumeDeadLockTest2(a) throws Exception {
		// Start the thread
		Thread consumerThread = new Thread(() -> {
			if (baozidian == null) {
				System.out.println("1. No buns, enter waiting");
				try { // Add a little delay for this thread
					Thread.sleep(5000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				// Suspend execution is performed after resume
				Thread.currentThread().suspend();
			}
			System.out.println("2. Buy the steamed stuffed bun and go home");
		});
		consumerThread.start();
		// After 3 seconds, produce one bun
		Thread.sleep(3000L);
		baozidian = new Object();
		consumerThread.resume();
		System.out.println("3. Inform consumers");
		consumerThread.join();
	}
Copy the code

Wait/notify mechanism

These methods can only by the holder of the same object lock thread calls, which is written inside the synchronized block, otherwise you will be thrown IllegalMonitorStateException anomalies. The wait method causes the current thread to wait, join the wait set for the object, and waive the currently held object lock. The notify/notifyAll method wakes up one or all threads that are waiting for this object lock. Note: The wait method is automatically unlocked, but in a certain order. If you wait until notify is called, the thread will always be WAITING.

Wait /notify code examples

/** Wait /notify */
	public void waitNotifyTest(a) throws Exception {
		// Start the thread
		new Thread(() -> {
			synchronized (this) {
				while (baozidian == null) { // If there is no bun, enter the wait
					try {
						System.out.println("1. Enter the wait");
						this.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			System.out.println("2. Buy the steamed stuffed bun and go home");
		}).start();
		// After 3 seconds, produce one bun
		Thread.sleep(3000L);
		baozidian = new Object();
		synchronized (this) {
			this.notifyAll();
			System.out.println("3. Inform consumers"); }}Copy the code

Example of deadlock causing

/** wait/notify */
	public void waitNotifyDeadLockTest(a) throws Exception {
		// Start the thread
		new Thread(() -> {
			if (baozidian == null) { // If there is no bun, enter the wait
				try {
					Thread.sleep(5000L);
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}
				synchronized (this) {
					try {
						System.out.println("1. Enter the wait");
						this.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			System.out.println("2. Buy the steamed stuffed bun and go home");
		}).start();
		// After 3 seconds, produce one bun
		Thread.sleep(3000L);
		baozidian = new Object();
		synchronized (this) {
			this.notifyAll();
			System.out.println("3. Inform consumers"); }}Copy the code

Park/unpark mechanism

The thread calls park and waits for “permission”. The unpark method provides “permit” to the specified thread without requiring the order in which the park and unpark methods are called. After calling unpark several times and calling park again, the thread will run directly. But there is no superposition, that is, the first call to the Park method will get “permission” to run directly, and subsequent calls will wait.

/** Normal park/unpark */
	public void parkUnparkTest(a) throws Exception {
		// Start the thread
		Thread consumerThread = new Thread(() -> {
			while (baozidian == null) { // If there is no bun, enter the wait
				System.out.println("1. Enter the wait");
				LockSupport.park();
			}
			System.out.println("2. Buy the steamed stuffed bun and go home");
		});
		consumerThread.start();
		// After 3 seconds, produce one bun
		Thread.sleep(3000L);
		baozidian = new Object();
		LockSupport.unpark(consumerThread);
		System.out.println("3. Inform consumers");
	}
Copy the code

Example of deadlock causing

/** deadlocked park/unpark */
	public void parkUnparkDeadLockTest(a) throws Exception {
		// Start the thread
		Thread consumerThread = new Thread(() -> {
			if (baozidian == null) { // If there is no bun, enter the wait
				System.out.println("1. Enter the wait");
				// The current thread takes the lock and suspends it
				synchronized (this) {
					LockSupport.park();
				}
			}
			System.out.println("2. Buy the steamed stuffed bun and go home");
		});
		consumerThread.start();
		// After 3 seconds, produce one bun
		Thread.sleep(3000L);
		baozidian = new Object();
		// Restore the consumerThread after securing the lock
		synchronized (this) {
			LockSupport.unpark(consumerThread);
		}
		System.out.println("3. Inform consumers");
	}
Copy the code

Pseudo awaken

Warning! The previous code used if statement to determine whether to enter the wait state, is wrong! The official recommendation is to check for wait conditions in the loop, because threads in the wait state may receive error alerts and false awakenings, and if wait conditions are not checked in the loop, the program will exit without meeting the end condition.

Pseudo wake up refers to the fact that the thread is not woken up because of API calls such as notify, NotifyAll, and unpark, but because of lower-level causes.