This is the 29th day of my participation in Gwen Challenge

1. Processes and threads

1.1 process

A process is a running program in the system. It is the basic unit of resource allocation. Each process has its own address space.

Process Control Block (PCB) describes the basic information and running state of a Process. The so-called creation and cancellation of a Process refer to the operation of PCB.

1.2 the thread

Thread is the basic unit of independent scheduling. It is contained in the process and is the actual operating unit of the process. There can be multiple threads in a process, and all threads share the resources of the process.

1.3 The difference between a process and a thread?

  1. Both processes and threads are concurrent units. The fundamental difference is that processes do not share common memory, but threads share process resources.
  2. From a system perspective, a process is a stand-alone piece of software that runs in its own virtual memory space. The system separates processes in memory so that if one process fails, it does not interfere with common memory and drag down other processes. Therefore, processes are typically isolated and collaborate through interprocess communication, which is defined by the operating system as an intermediate API.
  3. Thread is a part of the application program, and other threads of the same program to share the common memory, through the common memory to reduce the memory overhead, can faster exchange data and collaboration between threads;

1.4 Thread Classification

Java thread consists of two kinds, one is the user thread, one is the daemon thread;

1.4.1 Daemon threads

  1. Characteristics of daemon threads

Daemon thread is a special thread that is mainly used for background scheduling and support work in programs. The daemon thread terminates with the JVM only when there are no non-daemon threads in the Java VIRTUAL machine.

  1. Typical daemon threads in Java

GC (Garbage collector)

  1. How do I set up a daemon thread
Thread.setDaemon(true);
Copy the code

Note: Daemon properties need to be set before starting a thread, not after;

2. How do I create and run a thread instance

The Thread class is essentially an instance that implements the Runnable interface and represents an instance of a Thread. There are generally two ways to create thread instances:

2.1 Subclass And rewrite Threadrun()

public class MyThread extends Thread {
    @Override
    public void run(a){
        System.out.println("MyThread running"); }}Copy the code

The run() method is executed after the start() method is called, and the start() method returns as soon as the thread starts, rather than waiting for the run() method to finish executing.

MyThread myThread = new MyThread();
myThread.start();
Copy the code

2.2 Implementing the Runnable Interface

public class MyRunnable implements Runnable{
    @Override
    public void run(a){
        System.out.println("MyRunnable running"); }}Copy the code

The Runnable interface is implemented when creating a new class, and the MyRunnable instance object is passed in the constructor of the Thread class. Finally, the start() method is used.

Thread thread = new Thread(new MyRunnable());
thread.start();
Copy the code

3. Runnable vs Callable

3.1 a Runnable

This interface represents units of computation that must be run in a separate thread, with only the run() method, which is not allowed to return a value or throw an unchecked exception;

3.2 Callable

This interface represents a task with a return value, and there is only one call() method, which can return a value (or void) and also support throwing exceptions. Callable is typically used in ExecutorService cases to start asynchronous tasks, and then call the returned instance to get its value.

4. Concurrency vs. parallelism

4.1 concurrent

Multiple tasks are executed at the same time (not necessarily at the same time in a unit time).

4.2 the parallel

In unit time, multiple tasks are executed simultaneously;

5. Four thread pools

The top-level interface of a Java thread pool is Executor, but it is not strictly a thread pool, but rather a tool for executing threads. The real thread pool interface is ExecutorService, and there are generally four thread pools:

5.1 newCachedThreadPool

Create a thread pool that creates new threads as needed, but reuses previously constructed threads as they become available. For programs that perform many short-term asynchronous tasks, these thread pools can often improve program performance. Calling execute reuses previously constructed threads (if available), or creates a new thread and adds it to the thread pool if none is available. Threads that have not been used for 60 seconds are removed from the cache. So a thread pool that remains idle for a long time does not use any resources.

5.2 newFixedThreadPool

Create a reusable thread pool with a fixed number of threads to run in a shared unbounded queue. At any point in time, most nThreads will handle the active state of the task. If the attached task is submitted while all threads are active, the attached task will wait in the queue until there are available threads. If any thread terminates due to failure during execution prior to closure, a new thread will perform subsequent tasks in its place. Threads in the pool persist until a thread is explicitly closed.

5.3 newScheduledThreadPool

Create a thread pool that can schedule commands to run after a given delay or to execute them periodically.

5.4 newSingleThreadExecutor

Executors. NewSingleThreadExecutor () returns a thread pool (the thread pool threads only), after the death of the thread pool can be posted in a thread (or anomalies) to start a thread to replace the original thread continue execution;

6. Thread life cycle and status

6.1 Thread Status

state instructions
NEW Initial state, created thread, but not yet calledstart()methods
RUNNABLE Executable states, “ready” and “Running” are collectively called “Running”
BLOCKED Blocked state, indicating that the thread is blocked on the lock
WAITING Wait state, the staging state of a thread. Entering this state indicates that the current thread needs to wait for notification or interrupt from another thread
TIME_WAITING Timeout wait state, different fromWAITINGAfter the specified time can return automatically
TERMINATED Terminated: indicates that the thread is finished

The image above, from The Art of Concurrent Programming in Java, shows the state transition process for a thread:

The thread is created in the NEW state, then starts RUNNING by calling the start() method, then is in the READY state, and then is in the RUNNING state once the runnable thread has acquired the CPU time slice. If a thread executes wait(), it enters a WAITING state. A thread pool in WAITING needs to be notified by another thread before it can return to RUNNING. The TIME_WAITING state is equivalent to adding a timeout limit to the WAITING state. After the timeout, the thread will automatically return to the RUNNABLE state. When a thread invokes a synchronous method, it is BLOCKED if no lock is acquired. At the same time, if the thread executes RUNNABLE’s run() method, it enters the TERMINATED state.

6.2 Three cases of thread blocking

When a thread relinquishes CPU usage for some reason, it temporarily stops RUNNING until the thread enters the Runnable state. Generally speaking, blocking can be divided into the following three types:

  1. Object.wait -> wait queue

After the RUNNING thread executes object.wait (), THE JVM places the thread in waitting Queue;

  1. Synchronous blocking (lock -> lock pool)

When a thread in the RUNNING state acquires a lock on an object and the lock is occupied by another thread, the JVM adds the thread to the lock pool.

  1. Other blocking (sleep/join)

When a Thread in the RUNNING state executes thread.sleep (long ms) or thread.join (), or makes an I/O request, the JVM puts the Thread in the blocked state. When the sleep() state times out, join() waits for the thread to terminate or timeout, or the I/O process completes, and the thread returns to the RUNNABLE state.

6.3 Three ways a thread can die

  1. The end of the normal

After the run() or call() method completes, the thread terminates normally;

  1. Abnormal end

The thread throws an uncaught Exception or Error, causing the thread to terminate abnormally;

  1. Call the stop ()

Terminating a thread by calling its stop() method is not recommended because it can cause deadlocks.

6.4 Four Ways to Terminate a Thread

  1. Normal operation end

When the program ends, the thread ends automatically.

  1. Exit the thread with the exit flag

Normally, after the run() method is executed, the thread terminates normally. However, some threads are servos that run for a long time until certain external conditions are met. The exit flags are typically synchronized using the volatile keyword. Only one thread can modify the value of the exit flag at a time.

public class MyThread extends Thread{
    public volatile boolean flag = false;
    @Override
    public void run(a){
        while(!flag){
            ……
        }
    }
}
Copy the code
  1. The Interrupt method terminates a thread

There are two ways to terminate a thread using the interrput() method:

  • The thread is blocked

If sleep, wait, receive, accept, etc. are used, the thread is blocked. When the interrupt() method of the thread is called, an InterrputException is thrown. Whichever method of the blocking thread throws this exception is caught by the code, and break breaks out of the loop, giving us a chance to terminate execution of the thread. Instead of calling the Interrput () method to terminate the thread, we need to catch InterruptException and break out of the loop to terminate the run() method.

  • The thread is not blocked

If interrupted () is used, the interrupt flag is set to true, just as if interrupted () is used to control the loop.

public class ThreadSafe extends Thread {
    public void run(a) {
        // Exit a non-blocking process by judging the interrupt flag
        while(! isInterrupted()){try{
                // The blocking procedure catches the interrupt exception to exit
                Thread.sleep(5000);
            }catch(InterruptedException e){
                e.printStackTrace();
                // When an exception is caught, break out of the loop
                break; }}}}Copy the code
  1. The stop method terminates the process (thread unsafe)

Thread.stop() can be used to forcibly terminate a Thread, but when stop() is called, creating a child Thread raises a ThreadDeathError error and releases any locks held by the child Thread. Any block of code that locks is used to protect data consistency. If a call to Thread.stop() results in the sudden (uncontrolled) release of all locks held by the Thread, the protected data may become inconsistent, and other threads may use the corrupted data. It can lead to some strange application errors.

7. Context switch

7.1 Context Switch Activities

In multithreaded programming, the number of threads is generally more than the number of CPU cores, but a CPU can only be occupied by one thread at any time. To solve this problem, the CPU allocates time slices to each thread and turns the wheel. When a thread’s time slice runs out, the thread enters the ready state and the CPU is freed up by another thread. This process is called a context switch.

To sum up: the current thread will save its state before switching to another thread after executing the CPU time slice, so that the next time switching back to the thread can quickly load the state of the thread. The thread’s process from save to reload is a context switch.

As shown in the figure above, the steps of context switching can be summarized as follows:

  1. Suspend a process and store the process’s state (context) in the CPU somewhere in memory.
  2. Then retrieve the context of the next process in memory and restore it in the CPU register;
  3. Jump to where the program counter points (that is, to the line of code where the process was interrupted) and then resume the process;

7.2 Causes of Context Switchover

Context switching can be caused by one of the following:

  1. After the time slice of the current task is used up, the system CPU schedules the next task.
  2. If the current task encounters I/O blocking, the scheduler suspends the task and continues to the next task.
  3. Multiple tasks preempt lock resources. If the current task fails to grab lock resources, it is suspended by the scheduler and continues to the next task.
  4. User code suspends the current business, freeing up CPU time.
  5. Hardware interrupt;

Lock 8.

8.1 Classification of locks

There are many types of locks in Java, which can generally be classified as follows:

8.1.1 optimistic locking

Optimistic locking is a kind of optimistic thinking, thinking that read more than write less, the possibility of concurrent write is low, every time you go to fetch data, you assume that others will not modify, so you will not lock. But in the update will judge whether others have to update the data during this period, take in the write to read the current version number first, and then lock operation (compare with the last version number, if the same update), if the failure to repeat read – compare – write operation.

Optimistic locking in Java is basically implemented through CAS operation, which is an updated atomic operation used to compare the current value and the passed value. If the current value is the same as the passed value, it will be updated. Otherwise, it will fail.

8.1.2 pessimistic locks

Pessimistic locking is a pessimistic idea that writes more and reads less, and the likelihood of encountering concurrent writes is higher. Every time someone tries to read or write the data, they lock it because they think someone else will change it. In this way, when someone tries to read or write the data, they block and get the lock directly.

Pessimistic lock in Java is Synchronized. A lock in the AQS framework will first try the CAS optimistic lock to obtain the lock. If it fails to obtain the CAS optimistic lock, it will be converted to pessimistic lock.

8.1.3 spin lock

  1. The principle of spin locking

If the thread holding the lock can lock is released in a relatively short time resource, the thread of the lock those waiting for competition is don’t need to do between kernel mode and user mode switch will enter the blocked pending state, they just need to wait for a period of time (spin), waiting for the thread holding the lock releasing the lock can then immediately release the lock, to avoid the consumption of user and kernel thread switching.

  1. Advantages and disadvantages of spinlocks

Spinlocks can reduce thread blocking as much as possible, and there is a significant performance improvement for code blocks that are not competitive for locks and have a very short lock duration. Because the spin cost would be less than the cost of blocking, suspending, and then waking up operations that would cause the thread to make two context switches.

However, if the lock is hotly contested, or if the thread holding the lock needs to hold the lock for a long time to execute a synchronized block, it is not appropriate to use the spin lock, because the spin lock will occupy the CPU until the lock is acquired. At the same time, a large number of threads compete for a lock, which will lead to a longer time to acquire the lock, the consumption of thread spin is far greater than the consumption of thread blocking and suspending operations, and other threads that need CPU can not obtain CPU, resulting in a waste of CPU, we should close the spin lock.

8.1.4 Synchronized

Synchronized can regard any non-null object as a lock, which is an exclusive pessimistic lock and a reentrant lock.

  • Range of Synchronized
    1. When applied to a method, the object instance is locked (this);
    2. Locks when applied to static methodsClassInstance,ClassThe related data of the method is stored in the PermGen, which belongs to the global shared area. Therefore, the static method lock is equivalent to a global lock of the class, which will lock all the threads calling the method.
    3. synchronizedWhen applied to an object instance, it locks all blocks of code that are locked on that object. There are multiple queues, and when multiple threads access an object monitor together, the object monitor stores these threads in different containers;
  • Synchronized core components
component instructions
Wait Set callwait()The thread repository where the method is blocked
Contention List Competition in the queueAll threads requesting locks are first placed on the competing queue
Entry List Threads in the competing queue that qualify as candidate resources are moved toEntry List
OnDeck At any time,At most one thread is competing for the lock resource, and that thread is calledOnDeck
Owner The thread that has acquired the lock resource
! Owner The current thread that releases the lock
  • Synchronized implementation

  1. The JVM fetches data one at a time from the end of the wait queue for lock contention candidates (OnDeck), but in the concurrent case,Contention ListThe CAS is accessed by a large number of concurrent threads, at which point the JVM moves some threads to reduce contention for elements at the tail of the queueEntry ListAs a candidate contention thread;
  2. OwnerThe thread inunclockWhen willContention ListSome threads are migrated toEntry ListAnd specify one of the threads asOnDeckThread (usually the first thread to enter);
  3. OwnerThreads do not directly pass locks toOnDeckThreads instead give lock contention rightsOnDeckIt needs to re-compete for locks. While sacrificing fairness to some extent, it can greatly improve system throughput. In the JVM, this choice behavior is called “competitive switching”.
  4. OnDeckWhen the thread acquires the lock resource, it becomesOwnerThreads that have not acquired the lock resource remain atEntry ListIn the. ifOwnerThe thread iswait()Method blocks, then passes toWait SetQueue up until a certain pointnotify()/notifyAll()Wake up, then re-enterEntry List;
  5. In aContention List, Entry List, Wait SetThe threads are all in the blocking state, which is completed by the operating system.
  6. Synchronized Unfair lock.SynchronizedIn thread entryContention ListWhen,The waiting line first attempts to spin to acquire the lock, and enters if it cannotContention List;

8.2 Lock optimization

  1. Reduce lock holding time

Lock only on programs that require thread-safety;

  1. Reduce lock granularity

Splitting large objects (accessed by multiple threads) into smaller objects greatly increases parallelism and reduces lock contention. By reducing lock competition and favouritism, the success rate of lightweight locks can be improved. A typical case is ConcurrentHashMap.

  1. Lock the separation

The most common lock separation is the read-write lock ReadWriteLock, which is separated into read and write locks according to its functions. In this way, read and write locks are not mutually exclusive, but mutually exclusive, which can ensure thread safety and improve performance.

  1. Lock coarsening

In order to ensure effective concurrency between multiple threads, each thread is required to hold the lock for as short a time as possible, that is, the lock should be released immediately after the use of common resources. However, if the same lock is repeatedly requested, synchronized, and released, it consumes system resources without taking advantage of performance optimization.

  1. Lock elimination

When the just-in-time compiler finds objects that cannot be shared, it can eliminate the locking of those objects.

8.3 Thread deadlocks

  1. define

A thread deadlock is when multiple threads are blocked at the same time, and one or all of them are waiting for a resource to be released. Because the thread is blocked indefinitely, the program cannot terminate properly.

  1. Prerequisites for deadlock generation
  • Mutually exclusive conditions: resources are occupied by only one thread at any time;
  • Request and hold conditions: when a process is blocked by requesting resources, it holds on to acquired resources.
  • Non-preemption condition: the resources that have been acquired by the thread cannot be forced by other threads before being used up. The resources can only be released after being used up.
  • Circular waiting condition: the circular waiting resource relationship between several threads is formed.

8.4 How can I Avoid deadlocks

To generate a deadlock, all four conditions must be met, so to avoid a deadlock, we only need to apply one of the four conditions;

  1. Break the mutex condition

It is not feasible to artificially break a mutex condition, because the purpose of a lock is to make it mutex.

  1. Break request and hold conditions

We simply apply for all resources at once;

  1. Break the non-preemption condition

If a thread that occupies some resources fails to apply for other resources, it can release the occupied resources.

  1. Break the loop wait condition

Apply for resources in order to prevent it. If resources are released in a certain order, they will be released in reverse order, thus destroying the waiting conditions of the cycle.

9. Common thread methods

9.1 Basic Methods

methods instructions
wait() Enter after callWAITINGState, which is returned only after waiting for notification from another thread or being interrupted, and after callingLocks that are held by objects are released, typically used to synchronize methods or blocks of code
sleep() Enter after callTIMED-WAITINGState, after the callLocks held by objects are not released
yield() Make the current threadAllocate CPU execution time slicesTo recompete with other threads for CPU time slices. In general, the higher the priority, the more likely it is to compete for CPU time slices
interrupt() Interrupt the threadGiving a notification signal to a thread affects an interrupt flag bit within the thread, and the process itself does not change state
join() Wait for another thread to terminate, calling a thread in the current threadjoin()Method, the current thread becomes blocked, returns to another thread, and the current thread changes from blocked state to ready state, waiting for CPU resources
notify() Wake up a single thread waiting on this object monitor,If all threads are waiting on this object, one of them is chosen to wake up, and the selection is arbitrary and occurs when a decision is made about the implementation
idDaemon() Determines whether a thread is a daemon thread
isAlive() Determines whether a thread is alive

9.2 Differences between sleep() and Wait (

  1. sleep()Belong toThreadClass,wait()Belong toObjectClass;
  2. sleep()Causes the program to pause execution for a specified time, freeing the CPU for another thread, butIts monitoring status remains, the specified time will automatically restore the running state;
  3. callsleep()Method,The thread does not release the object lock; But the callwait()Method,The thread gives up the object lock and goes into the pool of wait locks waiting for the object, only for that objectnotify()Method the local site enters the object lock pool to obtain the object lock and enter the running state;

9.3 Difference between start() and run()

  1. start()Method is used to start a thread, truly implementing multithreading without waitingrun()After the method body is executed, the following code can be directly continued;
  2. By calling theThreadOf the classstart()Method to start a thread when the thread is inThe ready state, is not running;
  3. methodsrun()It’s called the thread body, and it basically contains the contents of the thread to execute, and the thread entersRunning status, start runningrun()Code in a method.run()Method completes, the thread terminates, and the CPU schedules another thread.

9.4 ThreadLocal (ThreadLocal storage)

ThreadLocal, also known as thread-local variables, is used to provide local variables within a thread that operate throughout the thread’s life cycle, reducing the complexity of passing common variables between methods or components within the same thread.

9.5 The difference between volatile and synchronized

Volatile addresses the memory visibility problem by allowing all reads and writes to volatile variables to be written directly to main memory, thereby ensuring visibility.

Synchronized solves the problem of execution control by preventing other threads from acquiring the monitor lock on the current object, thus making the block of code protected by the synchronized keyword in the current object inaccessible to other threads, i.e. unable to execute concurrently. Synchronized also creates a memory barrier, and the memory barrier instruction ensures that all CPU operations are flushed directly into main memory, thereby ensuring the memory visibility of the operation, and also causes all operations of the lock thread to happens-before those of the subsequent thread to acquire the lock.

The differences between the two are as follows:

  1. Volatile essentially tells the JVM that the value of the current variable in the register (working memory) is indeterminate and needs to be read from main memory; Synchronized locks the current variable so that only the current thread can access it and other threads are blocked.
  2. Volatile can only be used at the variable level; Synchronized can be used at the variable, method, and class levels
  3. Volatile only enables change visibility of variables, not atomicity. Synchronized can guarantee the change visibility and atomicity of variables
  4. Volatile does not block threads; Synchronized can cause threads to block.
  5. Volatile variables are not optimized by the compiler; Variables of the synchronized tag can be optimized by the compiler.