Detailed summary of Java multithreading

  • 2020-04-01 02:18:53
  • OfStack

I. the difference between thread.start () and thread.run ()

Start a Thread by calling the start() method of the Thread class, which is in the ready state and not running. The Thread class then completes its operation by calling the method run(), called the Thread body, which contains the contents of the Thread to be executed. The run method ends, the Thread terminates, and the CPU runs another Thread.

If you directly use the Run method, this is just to call a method, the program is still only the "main thread" this thread, did not create a new thread, the program execution path is still only one, so did not achieve the purpose of the writer thread.

The test code is as follows


public class MyThread implements Runnable {
public void run() {
System.err.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread thread = new MyThread();
Thread t1 = new Thread(thread, "Thread-1");
Thread t2 = new Thread(thread, "Thread-2");
t1.run();
t2.run();
}
}

The output result is

> > The current process is: main

> > The current process is: main

Use the start method instead:


package thread;
public class MyThread implements Runnable {
public void run() {
System.err.println(">> The current process is: "+Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread thread = new MyThread();
Thread t1 = new Thread(thread, "Thread-1");
Thread t2 = new Thread(thread, "Thread-2");
t1.start();
t2.start();
}
}

The result is:

> > The current process is: thread-1

> > The current process is: thread-2

Second, ThreadLocal class details

It's easy to take ThreadLocal for granted as a "local thread." In fact, ThreadLocal is not a Thread, but a local variable of a Thread, which might be easier to understand by calling it ThreadLocalVariable. When a variable is maintained using ThreadLocal, ThreadLocal provides a separate copy of the variable for each thread that USES the variable, so each thread can change its own copy independently without affecting the counterparts of other threads.

Here are the key points for ThreadLocal variables:

A ThreadLocal variable conveniently provides a separate variable for each thread. Multiple threads can manipulate the variable without affecting each other, because each thread is actually manipulating a copy of the change. A ThreadLocal instance typically appears as a static private static field in a class that is used to associate threads. When multiple threads access a ThreadLocal instance, each thread maintains a separate copy of the variable provided by ThreadLocal.

The following is the test code to test: three threads acting on an object to manipulate the same ThreadLoacl object (of type integer) to see if there are dirty reads and so on:


public class Test implements Runnable {
private static ThreadLocal<Integer> num = new ThreadLocal<Integer>();
public void run() {
num.set(0);
for (int i = 0; i < 3; i++) {
num.set(num.get() + 1);
System.out.println(Thread.currentThread().getName() + ":num="
+ num.get());
}
}
public static void main(String[] args) {
Test test = new Test();
Thread t1 = new Thread(test, "Thread-1");
Thread t2 = new Thread(test, "Thread-2");
Thread t3 = new Thread(test, "Thread-3");
t1.start();
t2.start();
t3.start();
}
}

The operation results are as follows:

Thread - 3: num = 1
Thread - 2: num = 1
Thread 1: num = 1
Thread - 2: num = 2
Thread - 3: num = 2
Thread - 2: num = 3
Thread 1: num = 2
Thread 1: num = 3
Thread - 3: num = 3

As you can see from the above, there are no dirty reads and so on at all, so ThreadLocal threads are safe.

Common usage: when the DAO class is a singleton class, the database connection (connection) is maintained independently by each thread.

Can be used to control the creation and use of sessions, such as ThreadLocal< Session> The session = new ThreadLocal< Session> (a);

Comparison between ThreadLoacal and synchronization mechanism:

1. In the synchronization mechanism, only one thread accesses a variable at a time through the object's locking mechanism. In this case, the variable is Shared by multiple threads. Using the synchronization mechanism requires the program to carefully analyze the complex problems such as when to read and write variables, when to lock an object, and when to release the object lock, etc., which are relatively difficult to design and write.

2, ThreadLocal, on the other hand, addresses concurrent access from multiple threads. ThreadLocal isolates multiple threads from conflicting access to the data by providing each thread with a separate copy of the variable. Since each thread has its own copy of the variable, there is no need to synchronize the variable. ThreadLocal provides a thread-safe Shared object that encapsulates unsafe variables into ThreadLocal when writing multithreaded code.

3, To summarize, for the problem of multi-threaded resource sharing, synchronization takes a time-for-space approach, while ThreadLocal takes a space-for-time approach. The former provides only one variable to be queued for access by different threads, while the latter provides one variable for each thread, so it can be accessed at the same time without affecting each other.

Three, InvalidMonitorStateException anomalies

Calling wait ()/notify ()/notifyAll () method of any one, did you get the object if the current thread lock, and then throws the exception IllegalMonitorStateException (i.e. program without any synchronized block or perform object synchronization method, still try calling wait ()/notify ()/notifyAll ()). Since this exception is a subclass of RuntimeExcpetion, it is not required to be caught (although you can catch as long as you want). As a RuntimeException, this type of exception is not mentioned in the method signature of wait(),notify(), and notifyAll().

In the following code, this exception occurs in the underlined part because no synchronization is performed on the object.


public class Common implements Runnable {
public synchronized void method1() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Method 1 called");
Thread.sleep(1000);
System.out.println("Method 1 done");
}
public synchronized void method2() throws InterruptedException {
Thread.sleep(1000);
System.err.println("Method 2 called");
Thread.sleep(1000);
System.err.println("Method 2 done");
}
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
try {
if (Thread.currentThread().getName().equals("Thread-1")) {
this.wait(1000);
method1();
} else {
method2();
notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Common c = new Common();
Thread t1 = new Thread(c, "Thread-1");
Thread t2 = new Thread(c, "Thread-2");
t1.start();
t2.start();
}
}

Change to the following code, the underlined part:

public class Common implements Runnable {
public synchronized void method1() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Method 1 called");
Thread.sleep(1000);
System.out.println("Method 1 done");
}
public synchronized void method2() throws InterruptedException {
Thread.sleep(1000);
System.err.println("Method 2 called");
Thread.sleep(1000);
System.err.println("Method 2 done");
}
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
try {
if (Thread.currentThread().getName().equals("Thread-1")) {
synchronized(this){
this.wait(1000);
}
method1();
} else {
method2();
synchronized (this) {
notifyAll();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Common c = new Common();
Thread t1 = new Thread(c, "Thread-1");
Thread t2 = new Thread(c, "Thread-2");
t1.start();
t2.start();
}
}

The difference between sleep () and wait () and suspend ()

A difference:

Sleep is a method of the Thread class, which is used by the Thread to control its own flow. For example, if there is a Thread to tell the time, and a time is printed in every second, then I need to add a sleep before the print method to make myself execute every second. It's like an alarm clock. Sleep () instructs the current thread to pause execution for a specified period of time, leaving execution to another thread, but the monitoring state remains and will resume automatically. Calling sleep does not release the object lock.

Wait is a method of the Object class that communicates between threads. This method makes the thread that currently has the lock wait until the other thread calls the notify method, but you can also give it a time to automatically wake up. This method is mainly used for scheduling between different threads. The object invokes the wait() method, causing the thread to abandon the object lock and enter the wait lock pool waiting for the object. Only after the notify method (or notifyAll) is issued against the object does the thread enter the object lock pool ready to acquire the object lock and enter the running state.

Difference between 2:
 
Calling the wait method releases the lock on the current thread. In fact, the communication between threads is managed by the object, and all threads operating on an object are managed by the object through its own wait method. As if this object is A TV set and three people are three threads, then the remote control of the TV set is the lock. If now A holds the remote control and the TV calls the wait method, then A will hand over his remote control, which will be scheduled by the jVM virtual machine.

Calling the sleep method does not release the lock, because sleep () is a thread's way of managing itself and does not involve thread communication. As in the above example, if A is holding the remote control, he can use his sleep to adjust the channel every ten minutes, and the remote control is still in his hand during the ten minutes when he is adjusting the channel and resting, so other people cannot get the remote control.

The suspend() method is prone to deadlocks. When suspend() is called, the target thread stops, but still holds the lock it acquired earlier. At this point, no other thread can access the locked resource unless the "suspended" thread resumes running. For any thread, it can cause a deadlock if they want to restore the target thread while trying to use any of the locked resources

The thread holding the lock releases the lock when:

1. Execute the synchronized code block.

2. During the process of executing the synchronized code block, the thread terminates due to an exception.

3. During the execution of the synchronized code block, the wait() method of the object to which the lock belongs is executed, and this thread releases the lock to wait for the object.

In the following cases, the thread stops execution, but the thread does not release the lock:

1. During the execution of the synchronized code block, the thread.sleep () method is executed, and the current Thread abandons the CPU and begins to sleep without releasing the lock.

2. During the execution of the synchronized block, the thread.yield () method is executed, and the current Thread abandons the CPU without releasing the lock.

3. During the execution of the synchronized block, another thread executes the suspend() method on the current object, and the current thread is suspended without releasing the lock.

Use synchronization on static methods

JAVA recognizes only two types of locks: object locks and class locks.

When a static method is synchronized, the "Class "object of that Class is acquired, so when a thread enters a synchronized static method, the thread monitor acquires the lock on the Class itself, locks the entire Class, and no other thread can enter any static synchronized method of that Class. It is not like an instance method, because multiple threads can access different instances simultaneously and synchronize the instance method. The test code is as follows:


public class Common implements Runnable {
public synchronized static void method1() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Method 1 called");
Thread.sleep(1000);
System.out.println("Method 1 done");
}
public synchronized static void method2() throws InterruptedException {
Thread.sleep(1000);
System.err.println("Method 2 called");
Thread.sleep(1000);
System.err.println("Method 2 done");
}
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
try {
if (Thread.currentThread().getName().equals("Thread-1")) {
method1();
} else {
method2();
// Thread.currentThread().notify();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
//The following code creates different threads on different objects to test whether there are locks for the same class
Common c1 = new Common();
Common c2 = new Common();
Thread t1 = new Thread(c1, "Thread-1");
Thread t2 = new Thread(c2, "Thread-2");
t1.start();
t2.start();
}
}

The implementation results are as follows:
Running Thread - 2
Running Thread - 1
Method 2 called
Method 2 done
Method 1 called
Method 1 done

Can two threads on an object call two different synchronized instance methods at the same time?

No, because an object has synchronized its instance methods, the thread acquires the object's object lock. So only after this method releases the object lock can the other synchronization methods be executed. The test code is as follows:


public class Common implements Runnable {
public synchronized void method1() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Method 1 called");
Thread.sleep(1000);
System.out.println("Method 1 done");
}
public synchronized void method2() throws InterruptedException {
Thread.sleep(1000);
System.err.println("Method 2 called");
Thread.sleep(1000);
System.err.println("Method 2 done");
}
public void run() {
System.out.println("Running " + Thread.currentThread().getName());
try {
if (Thread.currentThread().getName().equals("Thread-1")) {
method1();
} else {
method2();
// Thread.currentThread().notify();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Common c = new Common();
Thread t1 = new Thread(c, "Thread-1");
Thread t2 = new Thread(c, "Thread-2");
t1.start();
t2.start();
//The following code is used as a comparison to create different objects that are not subject to object locking
//Common c1 = new Common();
//Common c2 = new Common();
//c1.start();
//c2.start();
}
}

The implementation results are as follows:
Running Thread - 1
Running Thread - 2
Method 1 called
Method 1 done
Method 2 called
Method 2 done


Related articles: