preface: Although there are many articles about Java thread on blogs, I still want to write this series of blogs according to my own understanding, on the one hand to consolidate my basic knowledge, and on the other hand, if there is any misunderstanding, please kindly comment in the comments section
The concept of threads
What is a thread? This is a seemingly simple problem, but it involves a lot of basic computer knowledge. Before we talk about thread, we need to understand the concepts of process In official terms "thread process is the smallest unit of distribution of resources, is the smallest unit of the CPU scheduling" the answer is a bit abstract, don't know classmates looked likely to think of fog for an example of press close to life process can be seen as an independent team in the company, Team resources are allocated when the process is created, and teams generally manage their own work. Threads can be seen as team partners. A process can contain multiple threads (there are many partners in a team) 2. Processes consume more computer resources than threads (team management costs more, computer resources refer to the CPU, memory, disk IO, etc.) 3. Communication between processes is more difficult (communication between teams takes more time) ps(communication between processes includes pipes, message queues, shared memory, semaphores, sockets, etc.) 4. Data sharing between threads is relatively simple (communication within the team is more efficient) ps(Communication between Java threads has lock synchronization mechanism, shared memory, Wait /notify method in Object, etc.) 5. Between processes are independent of each other, thread may affect thread (for example, a thread of oom can directly lead to the virtual machine process crash) as the CPU process is more and more close to the bottleneck (vaguely remember like silicon crystal 3 nm is the ultimate) Moore's law in a different form to carry on its predictions, the core of the CPU for more and more. So master multithreading, make full use of CPU resources has been a programmer's necessary skills. Especially for large-volume task calculation, multi-threading can improve interface execution efficiency several times or even tens of times (the author also felt the charm of multi-threading in the actual development process).Copy the code
Creation of threads in Java
So how do you start a thread in Java? If you want to create a Thread, you must create a Thread object and call start. If you want to create a Thread, you must create a Thread object and call start.
The native keyword is used in collaboration with other languages, and tells the virtual machine that the implementation of this method was written in another language
The difference between the start() method and the run() method is that it tells the operating system to create a new thread to execute the run method, or it executes the run method directly from the current thread (make no mistake!!).
As for the specific code, there are three forms according to the Java standard writing method, I will use pseudo code to paste a little bit, of course, there are also the anonymous inner class writing method, lambada writing, these are syntax sugar, students who are interested in their own search.
1. Inherit Thread class, override run method, create ThreadStart instance, call start() method
Class ThreadStart extends Thread{@override public void run() {system.out.println (" ThreadStart extends Thread "); The thread started "); }}Copy the code
2. Implement a runnable interface that overrides the run method, creates a Thread instance and calls the start() method with the runnable object that overrides the run method as its constructor argument
class ThreadRunnableStart implements Runnable{ @Override public void run() { System.out.println(" Thread start method 2 (implement Runnable, override run method): thread start "); }}Copy the code
3. Implement the callable interface, override the call method, create FutureTask instance and take the callable object that overwrites the call method as the constructor parameter, create Thread instance and take fetureTask object as the constructor parameter, call start() method to start the Thread. Call FutureTask’s get() method to get the return value.
Ps (where the call method is not called by the thread’s start0 method, but by the FutureTask’s Run method)
class ThreadTaskStart implements Callable<String> { public String call() throws Exception { System.out.println(" Thread start method 3 (implement Callable, override call method): thread start "); Return "I am the return value of the call method "; }}Copy the code
Thread life cycle
Ps (picture cracked, next time to fill)
According to the thread cycle state diagram, the life cycle of a thread can be divided into the following five phases
- NEW(NEW) ps: the default state when the thread object is created
- Ps: After calling the start() method, the thread enters the ready stage, where it is scheduled by the CPU
- Ps: A thread enters the RUNNING state when it is allocated a CPU time slice
- Ps: BLOCKED when the suspend/sleep/wait method is called
- Ps: The state is TERMINATED when the state is TERMINATED or stop is called
FetureTask source code exploration
Future is an interface, and FutureTask is an implementation class of Future that implements Runnable, so FutureTask can be passed to the Thread object Thread to create a new Thread for execution.
FutureTask is designed to complement threads by letting you know exactly when a Thread is finished and get the results it returns when it is finished.
Here is a shared experience of the author: in the process of reading the source code, it is necessary to read with a problem, or there must be a main line
While reading the FetureTask source code, we can ask the following question: How is FetureTask implemented to return the result of thread execution?
We can start with a quick look at the core member variables defined in the class
private volatile int state; Private static final int NEW = 0; private static final int NEW = 0; Private static final int COMPLETING = 1; Private static final int NORMAL = 2; Private static final int exception = 2; Private static final int CANCELLED = 4; // Cancel private static final int interrupt = 5; Private static final int INTERRUPTED = 6; Private Callable<V> Callable; Private Object outcome; private Object outcome; private Object outcome; // Call method return value or exception private volatile Thread runner; // Work on the current thread of the task private Volatile WaitNode waiters; // Information about the thread waiting for the execution resultCopy the code
FetureTask implements the Runnable interface, so the run() method is called when a thread is started, so the run method is the core method of FetureTask.
The runAndReset() method can be called if the task needs to be repeated, resetting the state to NEW after each execution
Public void run() {// The runnerd method starts with a task status check and uses cas to attempt to assign a reference to the current thread object if (state! = NEW || ! UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c ! = null && state == NEW) { V result; boolean ran; Try {// Call the call method of callable and get the return value result = c.call(); // Execution status ran = true; } catch (Throwable ex) { result = null; ran = false; //state: NEW->COMPLETING->EXCEPTIONAL setException(ex); } Waiters (ran) // Initialize the work and waiters // State: NEW->COMPLETING->NORMAL set(result); }} finally {// Reset runner after execution. // Until the set state, runner is always non-empty, to prevent concurrent calls to the run() method runner = null; Int s = state; / / there are other threads to interrupt the current thread, the CPU is let out, spin wait if (s) > = INTERRUPTING handlePossibleCancellationInterrupt (s); }}Copy the code
Now that futureTask is done, how do we get the return value of the call method?
Before we look at the source code, we can deduce that futureTask has an execution state member variable and an outCome member variable that stores the return value. Just check state. If it’s finished, just return outCome. If it’s not finished, then you need to block the current thread and wake it up when it’s finished.
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (unit == null) throw new NullPointerException(); int s = state; AwaitDone if (s <= COMPLETING && (s = awaitDone(true, Ununit.tonanos (timeout))) <= COMPLETING) // Throw new TimeOutException (); return report(s); } //timed specifies whether a timed wait occurs. Private int awaitDone(Boolean timed, long nanos) throws InterruptedException { final long deadline = timed ? System.nanoTime() + nanos : 0L; WaitNode q = null; boolean queued = false; for (;;) If (thread.interrupted ()) {removeWaiter(q); if (thread.interrupted ()) {removeWaiter(q); throw new InterruptedException(); } int s = state; // Return if (S > COMPLETING) {if (q! = null) q.thread = null; return s; } else if (s == COMPLETING) // Cannot time out yet thread.yield (); Else if (q == null) q = new WaitNode(); // Cas joins the queue else if (! queued) queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q); Else if (timed) {nanos = deadline-system.nanotime (); if (nanos <= 0L) { removeWaiter(q); return state; } // Call the park method to block the thread (with wake up time) locksupport. parkNanos(this, nanos); } else // Call the park method to block the thread locksupport.park (this); }}Copy the code
This is the thread wait queue defined in futureTask, which is a one-way linked list that stores references to waiting threads. Because the GET method can be called concurrently, CAS is used to ensure thread-safety when the thread-wrapped WaitNode class is enqueued
CAS(Compare and swap) compares and replaces
When it comes to CAS, we have to mention optimistic and pessimistic locks in Java
Pessimistic locks assume that the data will be modified every time, so pessimistic locks will always hold the resource or data while holding the data, so that other threads will block when they want to request the resource, until the pessimistic locks release the resource. Synchronized in Java, for example, is a pessimistic lock.
Optimistic locks assume that data has not been modified each time, but are validated during write operations to ensure thread-safety (without this step… It’s not a lock.)
Cas is A type of optimistic lock. Cas has three operands —- memory object (V), expected original value (A), and new value (B). The principle of CAS is that when assigning A value to v object, it determines whether the original value is A. If it is A, the new value B is assigned to V object. If the original value is not A (indicating that the value of V has changed), no new value is assigned.
Of course, CAS also has some disadvantages, such as ABA problem, which can be solved by setting an additional version number, under juC there is an AtomicMarkableReference class implementation scheme, students who want to know can baidu ~
conclusion
This blog mainly introduces
1. The concept of threads
2.Java thread startup mode
3. Thread life cycle
4.FeatureTask source code
5. Cas introduction
In the next issue, I’m going to write about synchronized and Reentrantlock.