The foundation of Java multithreading programming — thread class

Time:2019-11-12

thread

When we read the program, it seems that we are tracking the processing flow of the program, but in fact, we are tracking the execution of the thread.

Single threaded program

In a single threaded program, there is only one process executed at a certain point in time.

When a java program is executed, at least one thread is running. The running thread is calledMain thread(Main Thread)。

Java programs run on the main thread,Background threadIt is also running, such as garbage collection thread, GUI related thread, etc.

The termination of Java program refers to DivisionDaemon threadAll threads except (daemon thread) are terminated. Daemons are threads that perform background jobs, such as garbage collection threads. We can set the thread as a daemon through the setdaemon () method.

Multithreaded program

A program composed of multiple threads is called a multithreaded program. When multiple threads are running, the running tracks of each thread will be intertwined, and there are multiple processes executed at the same time point.

Multithreaded application scenario:

  • GUI application: there are threads (UI threads) dedicated to Gui operations
  • Time consuming task: file and network I / O processing
  • Scenario of network server processing multiple client requests at the same time

P.S. uses classes in the java.nio package, and sometimes even without threads, it can perform I / O processing with both performance and scalability.

The difference between parallel and concurrent

There are sequential, parallel and concurrent modes in program running.

  • Sequential is used to indicate that multiple operations are processed in turn.
  • Parallelism is used to indicate that multiple operations are processed at the same time, depending on the number of CPUs.
  • Concurrency is used to indicate that an operation is divided into multiple parts and that unordered processing is allowed.

Concurrency is more abstract than order and parallelism. Concurrent processing of a single CPU is sequential execution, and concurrent processing of multiple CPUs can be executed in parallel.

If it is a single CPU, even if multiple threads are running at the same time, the concurrent processing can only be executed in sequence, switching between threads.

Concurrent processing includes sequential execution of concurrent processing and parallel execution of concurrent processing.

The foundation of Java multithreading programming -- thread class

The difference between thread and process

  • Shared memory between threads
    The biggest difference between processes and threads is whether memory is shared.
    Usually, each process has its own memory space. A process cannot read or write to the memory of other processes without authorization. Because the memory space of each process is independent, there is no need to worry about being damaged by other processes.
    Shared memory between threads makes the communication between threads more natural and simple. One thread writes content to the instance, and other threads can read the content of the instance. When more than one thread can access the same instance, mutual exclusion processing needs to be performed correctly.
  • Fast context switching of threads
    Another difference between processes and threads is the amount of heavy context switching.
    When the running process switches, the process temporarily saves its current state (context information). The process that then starts to run needs to recover its own context information saved before.
    When the running thread switches, the context switch will be performed as well as the process. But because the context information of thread management is less than that of process, generally speaking, the context switching of thread is faster than that of process.

When performing closely related tasks, threads are usually more appropriate than processes.

Advantages and costs of multithreaded programs

Advantage:

  • Make full use of hardware resources such as multi-core CPU, I / O equipment and network equipment to work in parallel.
  • Improve GUI application responsiveness, UI thread focuses on interface drawing and user interaction, and additionally enables threads to perform background tasks.
  • Network applications simplify modeling, and each client request is processed with a separate thread.

Disadvantages (cost):

  • Creating a thread consumes system resources and time, and prepares the thread’s private program counters and stacks.
  • The cost of thread scheduling and switching is the same. The context state information needs to be saved when the thread is switched out, so that the context state can be restored when the thread is switched back again.

Relatively speaking, if there are time-consuming tasks that need to be put into sub threads for actual execution, the cost of thread usage can be ignored.

The importance of multithreading

In addition to the hardware conditions satisfying the conditions of multithreading parallel execution, the program logic is also required to ensure that multithreading runs correctly, taking into account the mutually exclusive processing and synchronous processing between threads.

Class Thread

Thread creation and start

There are two ways to create and start threads:

  1. Use the subclass instantiation of thread class to create and start threads.
  2. Use the implementation class instantiation of runnable interface to create and start threads.

The steps of creating and starting a thread — Method 1:

  • Declare the subclass (extends Thread) of thread and override the run () method.
  • Create an instance of this class
  • Call the start() method of the instance to start the thread

Thread instance and thread itself are not the same thing. To create a thread instance, the thread does not start until the start() method is called. Similarly, even if the thread is terminated, the instance will not disappear. However, a thread instance can only create one thread. Once the start() method is called, no matter whether the thread ends normally or abnormally, a new thread cannot be created by calling the start() method again. And calling the start() method repeatedly throws an illegalthreadstateexception.

Thread run() method and start() method:

  • The run () method can be called repeatedly, but it will not start a new thread and execute in the current thread. The run () method is placed on the runnable interface to encapsulate the operation.
  • The start () method mainly performs the following operations: start the new thread and invoke the run () method in it.

The steps of creating and starting a thread — Method 2:

  • Declare the class and implement the runnable interface (implements runnable), which requires that the run () method must be implemented.
  • Create an instance of this class
  • Use this instance as a parameter to create an instance of thread classThread(Runnable target)
  • Call the start() method of an instance of the thread class to start the thread

Whether it is the method instantiated by the subclass of the thread class (1) or the method instantiated by the runnable interface (2), the method to start a new thread is the start () method of the thread class.

There is a single inheritance limit in Java. If a class already has a parent class, it can no longer inherit the thread class. At this time, you can create and start a new thread by implementing the runnable interface.

The thread class itself implements the runnable interface, and passes the override of the run () method to the subclass.

Properties of a thread

ID and name

adoptThread(String name)Construction method orvoid setName(String name), set a friendly name for thread to facilitate debugging.

priority

In the Java language, the priority of threads is from 1 to 10, and the default is 5. However, due to the different operating systems, the priority will be mapped to the value in the operating system. Therefore, the priority in Java language is mainly a suggestion. Do not rely too much on the priority in multithreaded programming.

State of thread

The thread.state enumeration type (enum) includes:

  • NEW
    After thread instantiation, the start() method has not been called to start.
  • RUNNABLE
    Operational status, running or ready to run.
  • BLOCKED
    Blocking status, waiting for other threads to release the instance’s lock.
  • WAITING
    Waiting status,InfiniteWait for another thread to perform a specific operation.
  • TIMED_WAITING
    Time limit wait state, waiting for other threads to perform the specified limited time operation.
  • TERMINATED
    End of thread run

Thread method

Currentthread() method

The static method currentthread() of the thread class returns the currently executing thread object.

Sleep() method

The static method sleep() of thread class can pause (sleep) the current thread (the thread executing the statement) and give up the CPU. Thread can be interrupted during sleep. The interrupt will be thrownInterruptedExceptionAbnormal. The parameters of the sleep () method are in milliseconds, but usually the JVM does not have precise control over time.

The sleep() method call needs to be placed in the try catch statement, which may throwInterruptedExceptionAbnormal.InterruptedExceptionException can cancel thread processing, and the thread in sleep state can be awakened in the middle by using interrupt() method.

The sleep () method is often used in multithreaded sample programs to simulate the processing of time-consuming tasks.

Yield () method

The static method yield() of thread class can pause the current thread (the thread executing the statement) and give the CPU priority to other threads. If there is no waiting thread, or the priority of the thread is not high, the current thread may continue to run, that is, the yield() method cannot ensure that the current thread is paused. The yield () method is similar to the sleep () method, but the pause time cannot be specified.

Join () method

The thread instance method of the thread class. The thread holding the thread instance will wait for the thread instance calling the join() method to end. The waiting period can be interrupted. The interrupt will be thrownInterruptedExceptionAbnormal.

Sample program:

public class HelloThread extends Thread {
    @Override
    public void run() {
        System.out.println("hello");
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new HelloThread();
        thread.start();
        thread.join();
    }
}

The main thread where the main() method is located will wait for the hellothread sub thread to execute the run() method before executing and exit the program.

Concurrent programming features

  • Atomicity
  • visibility
  • Orderliness

Atomic operation problems

The concept of atomicity comes from the database system. All operations in a transaction are either completed or not completed, and will not end at a certain link in the middle. If an error occurs during the execution of a transaction, it will be rolled back to the state before the transaction starts, just like the transaction has never been executed.

The atomicity of concurrent programming means that the operation of shared variables is indivisible. The assignment operation of java basic types except long and double is atomic operation.

Non atomic operations such as:

counter++;
  1. Read the current value of counter
  2. Add 1 to the current value
  3. Reassign the new value to counter

Java language solution:

  • Using the synchronized keyword
  • Usejava.util.concurrent.atomicpackage

Memory visibility issues

In computer structure, CPU is responsible for executing instructions and memory is responsible for reading and writing data. CPU’s execution speed is far faster than memory’s reading and writing speed, and cache is introduced to alleviate the speed inconsistency between them Copy the copy of memory data to cache in advance, which is convenient for CPU to use directly and quickly.

Therefore, in addition to memory, data in the computer may also be stored in CPU registers and various levels of cache. In this way, when accessing a variable, it may take priority to get it from the cache instead of memory; when modifying a variable, it may write the modification to the cache first, and then update it to memory synchronously.

It’s not a big problem for single threaded programs, but when multithreaded programs execute in parallel, the data in memory will be inconsistent, and the latest changes may not have been synchronized to memory. We need to provide a mechanism to ensure that the copies of shared variables in the multithreaded CPU cache are consistent with each other — cache consistency protocol.

Java language solution:

  • Using the volatile keyword
  • Using the synchronized keyword

If only to solve the memory visibility problem, the cost of using synchronized keyword is higher, and consider using volatile keyword in a more lightweight way.

Reordering of instructions

Order: that is, the order of program execution is strictly in accordance with the order of code execution.

Java allows compilers and processors to reorder instructions in order to improve efficiency. The reordering process will not affect the execution of single threaded programs, but may affect the correctness of multithreaded programs when they execute concurrently.

Volatile keyword details

Java usevolatileKeywords decorate variables to ensure visibility and order.

  • Ensure that the value of the variable is updated and written to memory as soon as it is modified, and read the value of the variable from memory by default. (visibility)
  • Prohibit instruction reordering (ordering)

howevervolatileKeywords do not guarantee that operations on variables are atomic.

Mutex processing of threads (synchronized keyword details)

Each thread has its own program counter (instruction execution line number), stack (method parameters, local variables, etc.), and multiple threads share the heap (object), which corresponds to the JVM memory model. When multiple threads operate on the objects in the heap area, the problem of multi-threaded shared memory may occur.

Race condition

Bank withdrawal
If (available balance is greater than or equal to withdrawal amount){

Available balance less withdrawal amount

}
When multiple threads operate at the same time, balance confirmation (available balance is greater than or equal to the withdrawal amount) and withdrawal (available balance minus the withdrawal amount) may be executed alternately, so the execution order between threads cannot be guaranteed.

Thread A Thread B
Is the available balance (1000) greater than or equal to the withdrawal amount (1000)? Yes Switch execution thread B
Thread a is waiting Is the available balance (1000) greater than or equal to the withdrawal amount (1000)? Yes
Thread a is waiting Available balance less withdrawal amount (1000-1000 = 0)
Switch execution thread a End of thread B
Available balance less withdrawal amount (0 – 1000 = – 1000) End of thread B

When multiple threads operate on the same object at the same time, race condition may occur, and the final execution result cannot be expected, which is related to the execution time sequence and requires “traffic control” – mutually exclusive processing of threads.

Java usesynchronizedKeyword to execute the mutex processing of a thread.synchronizedKeywords can modify instance methods, static methods, and code blocks of a class.

lock

The synchronized keyword protects objects rather than methods or code blocks, and locks are used to perform thread exclusive processing.

Synchronized modifies static methods and instance methods to protect different objects:

  • The synchronized decorated instance method uses the instance object this of this class.
  • Synchronized modifies the static method by using the class object class of the class.

Each object has an independent lock, which is shared by all synchronized methods within the same object.

The foundation of Java multithreading programming -- thread class

Precautions for synchronized method

Based on the synchronized keyword to protect the object principle, there are the following corollaries:

  • The synchronized method in an instance can only be run by one thread at a time, while the non synchronized method can be run by multiple threads at the same time.
  • Multiple synchronized methods in an instance cannot run with multiple threads.
  • The synchronized method in different instances can be run by multiple threads at the same time.
  • The static method (this object) and instance method (class object) modified by synchronized can be executed by multiple threads at the same time.
Synchronized method synchronous use

The synchronized method is reentrant, that is, after obtaining the lock, you can call other synchronized methods that need the same lock in one synchronized method.

Generally, when protecting an instance variable, set all the methods accessing the variable to synchronized synchronization method.

If you just want a part of the method to run by a thread, rather than the whole method, you can use synchronized code block to precisely control the execution scope of the mutex processing.

Synchronized method execution process
  1. Try to acquire the object lock. If the lock is acquired and enters 2, if the lock is not acquired, the waiting queue joining the lock will enter the blocking state and wait to be awakened.
  2. Executing the synchronized method
  3. Release the object lock. If there are threads in the waiting queue waiting to acquire the lock, wake it up. When there are multiple threads in the waiting queue, one cannot be explicitly awakened, and multiple threads compete to acquire it.

deadlock

Deadlock refers to the phenomenon that two or more processes (threads) wait for each other due to competing for resources during execution. Without external force, they will not be able to move forward.

Four necessary conditions for deadlock

  1. Mutually exclusive condition: refers to the exclusive use of resources allocated by a process, that is, a resource is occupied by only one process in a period of time. If there are other processes requesting resources at this time, the requester can only wait until the process occupying the resources has finished releasing.
  2. Request and hold condition: refers to that the process has kept at least one resource, but has made a new resource request, and the resource has been occupied by other processes. At this time, the request process is blocked, but it still keeps the other resources it has obtained.
  3. Non deprivation condition: refers to the resource obtained by the process, which cannot be deprived before it is used up, and can only be released by itself when it is used up.
  4. Circular waiting condition: when a deadlock occurs, there must be a process resource circular chain, that is, P0 in the process set {P0, P1, P2,…, PN} is waiting for a resource occupied by P1; P1 is waiting for a resource occupied by P2 , PN is waiting for resources already occupied by P0.

The above four conditions must be met simultaneously to generate deadlock. As long as any of these conditions is not tenable, deadlock can be avoided.

It should be avoided to apply for another lock while holding one. If you do need multiple locks, you should acquire them in the same order.

Thread Cooperation (wait(), notify() method details)

In addition to the mutually exclusive processing in the competition, multithreads also need to cooperate with each other. The premise of collaboration is to understand the shared conditional variables.

Wait(), notify(), notifyall() are instance methods of the object class, not methods in the thread class. These three methods are not so much for thread operations as for instance conditional wait queue operations.

Operation obj condition wait for the thread in the queue (wake up, wait):

  • Obj. Wait() puts the current thread into the conditional wait queue of obj.
  • Obj. Notify() wakes a thread from the conditional wait queue of obj.
  • Obj. Notifyall() wakes up all threads in the obj condition wait queue.

Wait () wait method

Each object has a lock and a lock waiting queue. In addition, there is a waiting queue representing conditions, which is used for cooperation between threads. Calling the wait() method will put the current thread into the condition queue to wait. The waiting condition needs waiting time or depends on other threads to change (notify() / notifyall()). The waiting period can also be interrupted. The interrupt will be thrownInterruptedExceptionAbnormal.

The main difference between the wait() method of the object class and the sleep() method of the thread class is whether the object lock is released. From the class to which the method belongs, we can see that the wait() method of the object class contains the object lock management mechanism.

  • Wait () instance method for inter thread communication and cooperation
  • The sleep() static method is used to pause the current thread
  • Both will give up CPU
Wait () method execution process
  • Put the current thread into the condition queue to wait and release the object lock.
  • Current thread inWAITINGTIMED_WAITINGStatus.
  • Wait time or wake up by another thread (notify() / notifyall()) to remove the wait thread from the condition queue.

    • Wake up thread gets object lock, enterRUNNABLEStatus, return from the wait () method, and re perform the wait condition check.
    • Wake up thread cannot get object lock, enterBLOCKEDStatus, join the waiting queue of object lock, and continue waiting.

Notify() wake up method

The difference between notify() and notifyall() methods

  • The notify () method wakes up a thread in the waiting queue.
  • The notifyall() method wakes up all threads in the waiting queue.

The notifyAll () method is usually used, which is more robust than the notify () method code, but it is slower to wake up multiple threads.

Note: after calling the notify () method, wake up the waiting thread in the condition queue and remove it from the queue. The awakened thread does not run immediately because the thread executing the notify () method still holds the lock and waits for the synchronized code block where the notify () method is executed to finish before releasing the lock. The waiting thread gets the lock from the wait () method and performs the wait condition check again.

Conclusion:

  • The thread must hold the lock of the instance to execute the above methods (wait(), notify(), notifyall())
  • Wait() / notify() method can only be called in synchronized code block. If wait() / notify() method is called and the current thread does not hold the object lock, an exception will be thrownjava.lang.IllegalMonitorStateException

Producer / consumer model application

  • Producer’s thread for generating data
  • Threads for consumers to use data

Producer threads and consumer threads collaborate through shared queues,

The producer / consumer model adds a bridge role between producers and consumers, which is used to eliminate the difference of processing speed between threads.

The foundation of Java multithreading programming -- thread class

The channel role holds the shared queue data, performs mutually exclusive processing on the access of the producer role and the consumer role, and hides the multi-threaded implementation.

Thread interrupt

The thread ends normally after the run() method is executed, but in practical application, the multithreading mode is often a dead cycle. Considering the special situation, it is necessary to cancel / close the thread. Java uses interrupt mechanism to transfer information through cooperation, thus canceling / shutting down threads.

Method of interruption

public static boolean interrupted()
public boolean isInterrupted()
public void interrupt()
  • Interrupt() and isinterrupted() are instance methods that are called through thread objects.
  • Interrupted() is a static method that is actually executed by the current thread, thread. Currentthread().

Thread has interrupted interrupt status flag, which is used to judge whether thread is interrupted.

  • The isinterrupted() instance method returns the interrupt status of the corresponding thread.
  • The interrupted() static method returns the interrupt status of the current thread, with side effect of clearing the interrupt status.

Interrupt response of different thread states

  • NEWTERMINATED
    Calling the interrupt() method has no effect
  • RUNNABLE
    Call interrupt() method, the thread is running, and has nothing to do with I / O operation, just set thread interrupt status flag. If the thread is waiting for I / O operations, special processing occurs.
  • BLOCKED
    Calling interrupt() method cannot interruptBLOCKEDThread in state
  • WAITINGTIMED_WAITING
    Call interrupt() method to set thread interrupt status flag, throwInterruptedExceptionAbnormal. This is a checked exception that the thread must handle.

Use of interruptions

For modules providing thread services, the cancel / close thread method should be encapsulated to provide external interfaces, rather than the caller calling the interrupt () method himself.

A comprehensive diagram of thread state transition

Combine the thread method (thread class + object class) to see the state transition of the thread:

Note:

  • Thread t = new thread(); the thread class calls static methods, and the t object calls instance methods.
  • Object o = new object(); object class calls static methods, and object o calls instance methods.
  • Running indicates the running state, not the thread.state enumeration type.

The foundation of Java multithreading programming -- thread class

appendix

Runnable interface and callable interface

  • The return value of the run() method provided by the runnable interface is void
  • The call() method provided by the callable interface returns a generic value

The callable interface is often used to cooperate with future and futuretask classes to obtain asynchronous execution results.