This series is based on the course of refining numbers into gold, and in order to learn better, a series of records were made. This article mainly introduces 1. What is a thread 2. Basic operations of threads 3. Daemon thread 4. Thread priority 5. Basic thread synchronization operations
1. What is thread
Threads are execution units within processes
There are several threads in a process.
A thread is an execution unit within a process.
The reason for using threads is that process switching is a very heavyweight operation and consumes resources. If you use multiple processes, the number of concurrency will not be very high. Threads are smaller scheduling units and are lighter, so threads are more widely used in concurrent design.
In Java, the concept of threads is similar to the concept of operating system-level threads. In fact, Jvm will map threads in Java to the thread area of the operating system.
2. Basic operations of threads
2.1 Thread status diagram
The above picture shows the basic operation of threads in Java.
When a thread is new, the thread actually does not work. It just generates an entity, and the thread is actually started when you call the start method of this instance. After startup, it reaches the Runnable state. Runnable means that the thread's resources and so on have been prepared and can be executed, but it does not mean that it is in the execution state. Due to the rotation of time slices, the thread may not be executing at this time. For us, the thread can be considered to have been executed, but whether it is executed is actually depends on the scheduling of the physical CPU. When the thread task is executed, the thread reaches the Terminated state.
Sometimes, during the execution of a thread, it is inevitable that some locks or object monitors will be applied. When it cannot be retrieved, the thread will be blocked, suspended, and reaches the Blocked state. If this thread calls the wait method, it is in a Waiting state. The thread entering the Waiting state will wait for other threads to notify it. After the notification is made, the Waiting state will switch from the Waiting state to the Runnable state to continue execution. Of course, there are two types of waiting states, one is to wait indefinitely until it is notified. It has been waiting for a limited period of time. For example, if you wait for 10 seconds or have not been notified, it will automatically switch to the Runnable state.
2.2 Create a new thread
Thread thread = new Thread();
thread.start();
This opens a thread.
One thing to note is
Thread thread = new Thread();
thread.run();
You cannot open a new thread by calling the run method directly.
The start method actually calls the run method on a new operating system thread. In other words, if you call the run method directly instead of the start method, it will not start a new thread, but will perform your operations in the current thread calling run.
Thread thread = new Thread("t1"){ @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()); }};thread.start(); If start is called, the output is t1Thread thread = new Thread("t1"){ @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()); }};thread.run(); If it is run, main is output. (Turning run directly is actually just an ordinary function call, and it has not achieved the role of multi-threading)
There are two ways to implement the run method
The first method is to directly override the run method. As shown in the code just now, the most convenient way can be achieved by using an anonymous class.
Thread thread = new Thread("t1"){ @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()); }}; The second way
Thread t1=new Thread(new CreateThread3());
CreateThread3() implements the Runnable interface.
In Zhang Xiaoxiang's video, the second method is recommended, saying that it is more object-oriented.
2.3 Terminate thread
Thread.stop() is not recommended. It releases all monitors
It has been clearly stated in the source code that the stop method is Deprecated, and the reason is also explained in Javadoc.
The reason is that the stop method is too "violent". No matter where the thread executes, it will immediately stop dropping the thread.
When the write thread gets the lock, starts writing data. After writing id = 1, it is stopped and the lock is released when preparing to set name = 1. The read thread obtains the lock for reading operation. The id read is 1 and the name is still 0, which causes data inconsistency.
The most important thing is that this kind of error will not throw an exception and will be difficult to detect.
2.4 Thread interrupt
There are 3 ways to interrupt thread
public void Thread.interrupt() // interrupt thread
public boolean Thread.isInterrupted() // Determine whether it is interrupted
public static boolean Thread.interrupted() // Determine whether it is interrupted and clear the current interrupt status
What is thread interruption?
If you do not understand the interrupt mechanism of Java, such an explanation is very likely to cause misunderstanding, and you believe that calling the interrupt method of the thread will definitely interrupt the thread.
In fact, Java interruption is a collaboration mechanism. In other words, calling the interrupt method of a thread object does not necessarily interrupt the running thread, it just requires the thread to interrupt itself at the right time. Each thread has a boolean interrupt state (not necessarily the object's property, in fact, this state is indeed not a Thread field), and the interrupt method simply sets the state to true. For non-blocking threads, the interrupt state is changed, that is, Thread.isInterrupted() will return true and will not stop the program;
public void run(){//Thread t1 while(true){ Thread.yield(); }}t1.interrupt(); This will not have any effect to interrupt thread t1, but the interrupt status bit is changed.
If you want to terminate this thread very gracefully, you should do this
public void run(){ while(true) { if(Thread.currentThread().isInterrupted()) { System.out.println("Interruted!"); break; } Thread.yield(); } }Using interrupts provides certain guarantees for data consistency.
For threads in cancelable blocking states, such as threads waiting on these functions, Thread.sleep(), Object.wait(), and Thread.join(), this thread will throw an InterruptedException after receiving the interrupt signal, and will set the interrupt state back to false.
For threads in unblocking states, you can write code like this:
public void run(){ while(true){ if(Thread.currentThread().isInterrupted()){ System.out.println("Interruted!"); break; } try { Thread.sleep(2000); } catch (InterruptedException e) { System.out.println("Interruted When Sleep"); //Set the interrupt status. The interrupt flag bit will be cleared after throwing an exception Thread.currentThread().interrupt(); } Thread.yield(); }}2.5 Thread hangs
Suspend and resume threads
suspend() will not release the lock
If the locking occurs before resume(), both methods of deadlock occurring are Deprecated methods and are not recommended.
The reason is that suspend does not release the lock, so no thread can access the critical area resource that is locked by it until it is resumed by other threads. Because the sequence of threads running cannot be controlled, if the resume method of other threads is run first, the suspended later will always occupy the lock, causing a deadlock to occur.
Use the following code to simulate this scenario
package test;public class Test{ static Object u = new Object(); static TestSuspendThread t1 = new TestSuspendThread("t1"); static TestSuspendThread t2 = new TestSuspendThread("t2"); public static class TestSuspendThread extends Thread { public TestSuspendThread(String name) { setName(name); } @Override public void run() { synchronized (u) { System.out.println("in " + getName()); Thread.currentThread().suspend(); } } public static void main(String[] args) throws InterruptedException { t1.start(); Thread.sleep(100); t2.start(); t1.resume(); t2.resume(); t1.join(); t2.join(); }} Let t1 and t2 compete for a lock at the same time, the thread that is competed for is suspended, and then resume. Logically speaking, a thread should be released by resume after competing for it, and then another thread competes for the lock and resumes.
The result output is:
in t1
in t2
This means that both threads compete for the lock, but the red light on the console is still on, which means that there must be threads that have not been executed in t1 and t2. Let's dump it and take a look
It was found that t2 has been suspended. This creates a deadlock.
2.6 join and yeild
yeild is a native static method. This method is to release the CPU time it owns and then compete with other threads (note that yeild's threads may still compete for CPU, pay attention to the difference from sleep). It is also explained in javadoc that yeild is a method that is basically not used and is generally used in debug and test.
The join method means waiting for other threads to end, just like the code in the suspend section, you want the main thread to wait for t1 and t2 to end before ending. If it doesn't end, the main thread will be blocked there.
package test;public class Test{ public volatile static int i = 0; public static class AddThread extends Thread { @Override public void run() { for (i = 0; i < 10000000; i++) ; } } public static void main(String[] args) throws InterruptedException { AddThread at = new AddThread(); at.start(); at.join(); System.out.println(i); }} If the at.join of the above code is removed, the main thread will run directly and the value of i will be very small. If there is a join, the value of the printed i must be 10000000.
So how is join implemented?
The nature of join
while(isAlive())
{
wait(0);
}
The join() method can also pass a time, which means waiting for a limited period of time, and automatically wake up after this time.
There is a question like this: Who will notify the thread? There is no place to call notify in the thread class?
In javadoc, a relevant explanation was found. When a thread is completed and terminated, the notifyAll method will be called to wake up all threads waiting on the current thread instance. This operation is done by jvm itself.
So javadoc also gave us a suggestion not to use wait and notify/notifyall on thread instances. Because jvm will call itself, it may be different from the expected result of your call.
3. Daemon thread
Silently completing some systematic services in the background, such as garbage collection threads and JIT threads, can be understood as daemon threads.
When all non-daemon processes end within a Java application, the Java virtual machine will exit naturally.
I have written an article on how to implement it in python, check it out here.
It is relatively simple to become a daemon in Java.
Thread t=new DaemonT();
t.setDaemon(true);
t.start();
This opens a daemon thread.
package test;public class Test{ public static class DaemonThread extends Thread { @Override public void run() { for (int i = 0; i < 10000000; i++) { System.out.println("hi"); } } } public static void main(String[] args) throws InterruptedException { DaemonThread dt = new DaemonThread(); dt.start(); }} When thread dt is not a daemon thread, after running, we can see the console output hi
When joining before start
dt.setDaemon(true);
The console exits directly and has no output.
4. Thread priority
There are 3 variables in the Thread class that define thread priority.
public final static int MIN_PRIORITY = 1;public final static int NORM_PRIORITY = 5;public final static int MAX_PRIORITY = 10;package test;public class Test{ public static class High extends Thread { static int count = 0; @Override public void run() { while (true) { synchronized (Test.class) { count++; if (count > 10000000) { System.out.println("High"); break; } } } } } public static class Low extends Thread { static int count = 0; @Override public void run() { while (true) { synchronized (Test.class) { count++; if (count > 10000000) { System.out.println("Low"); break; } } } } } public static void main(String[] args) throws InterruptedException { High high = new High(); Low low = new Low(); high.setPriority(Thread.MAX_PRIORITY); low.setPriority(Thread.MIN_PRIORITY); low.start(); high.start(); }} Let a high-priority thread and a low-priority thread compete for a lock at the same time and see which one completes first.
Of course, it does not necessarily have to be completed first with high priority. After running it multiple times, I found that the probability of high priority completion is relatively high, but it is still possible to complete it first with low priority.
5. Basic thread synchronization operation
synchronized and Object.wait() Obejct.notify()
For details of this section, please refer to a blog I wrote before.
The main thing to note is
There are three ways to lock synchronized:
Specify locked object: Lock the given object and obtain the lock of the given object before entering the synchronization code.
Directly acting on the instance method: it is equivalent to locking the current instance. Before entering the synchronization code, you must obtain the lock of the current instance.
Directly acting on static methods: it is equivalent to locking the current class. Before entering the synchronous code, you must obtain the lock of the current class.
If it works on the instance method, do not new two different instances
It works on static methods, as long as the class is the same, because the added lock is a class.class, and two different instances can be new.
How to use wait and notify:
Use whatever lock, call wait and notify
I won't go into details in this article.