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.