Processes and threads

process

  1. Is an execution of a program; The basic unit of operating system resource allocation
  2. Processes have their own independent address space. The system allocates address space to each process that is started

thread

  1. Is a subtask that runs independently in a process; It is the basic unit of the processor for task scheduling
  2. Threads in the same process share data such as global variables and static variables

Java thread

Creating a Thread

public class MyThread1 extends Thread{ @Override public void run() { System.out.println("1"); }}Copy the code
public class MyThread2 implements Runnable{ @Override public void run() { System.out.println("2"); }}Copy the code

The use of Thread class inheritance is limited because Java can only inherit from one class and does not support multiple inheritance. An additional class can be inherited by implementing the Runnable interface.

Common thread methods

Static void sleep(long millis) static void sleep(long millis) Void interrupt() marks a stop flag on the thread object calling the method (instead of the thread object executing the current code). 5. Static Boolean Interrupted () Returns whether the thread executing the current code is interrupted, 6. Boolean isInterrupted() returns whether the thread calling the method (rather than the thread object executing the current code) isInterrupted, 7. Long getId() obtains the thread ID 8. Int getPriority() obtains the priority of the threadCopy the code

Classification of thread

  • User threads

xx

  • Daemon thread
  1. The characteristics of

1) When there are no more non-daemon threads in the process, the daemon thread is destroyed automatically. A typical daemon thread is a garbage collector thread.

2) A new thread created in a daemon thread is also a daemon thread

3) The contents of the finally block cannot be relied on in the daemon thread to ensure that the logic to close or clean up a resource is performed. Reason: Once all user threads have finished running, the Daemon thread terminates with the JVM, so the finally block in the Daemon thread may not be executed.

  1. Set up the

Before calling the start() method of the thread object, call the setDaemon(true) method to set it up as a daemon thread

Thread state

  • State the category
public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link  #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }Copy the code
  • State transition diagram

Thread synchronization

  • Local variables do not have thread-safety issues and are therefore thread-safe.
  • Instance variables are thread-unsafe because it is highly likely that the instance variable is shared.

Thread safety

If multiple threads access a shared resource (resource contention), thread safety will occur. Therefore, you need to queue the threads one by one to execute the shared resource (synchronous execution) when multiple threads execute to the shared resource.

synchronized

  1. Synchronized: a reentrant lock that modifies methods and acts on blocks of code. Used to synchronize shared resources.

    Reentrant lock: It can acquire its own internal lock again. For example, thread A has acquired the lock on obj, but the lock has not been released yet. It can acquire the lock again when it wants to. Think about it. Synchronized locks are non-reentrant and cause deadlocks

  2. Synchronized modifies non-static methods, and the lock is applied to the current object. Modify a static method, then the lock is attached to the Class object. Synchronized (obj) modifies a block of code that requires the lock to be applied to the specified object. If obj is this, the current object is synchronized. If it is a Class object, it is added to the Class object. Note that if obj is a constant of type String, pay special attention to the exceptions brought by constant pools.
  3. Synchronized modified methods can be inherited by subclasses, but if A subclass rewrites the parent class’s synchronized method A, then A overridden in the derived class still needs to be synchronized with the synchronized keyword, otherwise it will not be synchronized.
  4. When does a thread acquire a lock and release it? 1) The synchronization code block is executed. 2) An exception occurs during the synchronization code block execution and the synchronization code block exits
  5. Each lock object has two queues, a blocking queue and a ready queue. The blocking queue stores the blocked thread, and the ready queue stores the thread that will acquire the lock.

ReentrantLock

  • ReentrantLock enables synchronized synchronization
class CountThread extends Thread{ ReentrantLock lock = new ReentrantLock(); @Override public void run() { try { lock.lock(); / /... }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); }}}Copy the code
  • ReentrantLock + Condition(wait/single/singleAll) can implement notification waiting mechanism

The biggest advantage of this approach is that it can selectively wake up blocked threads. Synchronized + wait()/wait(long) + Notify ()/notifyAll() either wakes up one thread randomly or wakes up all threads.

Copy the code
  • ReentrantReadWriteLock

ReentrantLock is thread-exclusive, meaning that only one thread is executing a shared resource at a time. This ensures thread-safe instance variables, but is very inefficient. ReentrantReadWriteLock speeds up code running.

A read-write lock means that there are two locks, one is read (shared) and one is write (exclusive). Multiple read locks are not mutually exclusive. Read locks are mutually exclusive with write locks, and write locks are mutually exclusive with write locks.

volatile

role

Make variables visible across multiple threads. Thread synchronization is a lightweight implementation.

How do you guarantee visibility

The thread accesses variables that are volatile, fetching and writing from shared memory each time

The difference between volatile and synchronized

  • The former can only modify variables; The latter can only modify methods and code blocks
  • The former does not block the thread; The latter blocks the thread
  • The former can guarantee data visibility, but not atomicity; The latter guarantees visibility and atomicity

A deadlock

Different threads are waiting for locks that can never be released, causing all tasks to fail

Four essential conditions

  1. Can’t be deprived

Resources acquired by a process cannot be seized by other processes before they are used up, but can only be released by the process that obtained the resources

  1. Request and Hold

A process that has retained at least one resource makes a request for a new resource that has already been occupied by another process. The requesting process is blocked but does not release the resource that it has acquired.

  1. The mutex

Processes require exclusive control over allocated resources, that is, a resource is held by only one process at a time. If another process requests the resource, the requesting process can only wait.

  1. Loop waiting for

There is a set of wait processes {P0, P1… Pn}, P0 is occupied by P1, and P1 is occupied by P2…… , the resource waiting for PN-1 is occupied by Pn, and the resource waiting for Pn is occupied by P0.

Detection of deadlock

jstack -l pid >> threadDump.txt

The sample

public class DeadThread extends Thread{ private boolean chooseFlag = false; private Object lock1 = new Object(); private Object lock2 = new Object(); @override public void run() {if (chooseFlag){synchronized (lock1){ System.out.println("chooseFlag=true get lock lock1"); try { Thread.sleep(2000); // Wait for the first thread to acquire lock2} catch (InterruptedException e) {e.printStackTrace(); Println ("chooseFlag=true "); synchronized (lock2){system.out.println ("chooseFlag=true "); }}else {synchronized (lock2){system.out.println ("chooseFlag=false "); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); Println ("chooseFlag=false "); synchronized (lock1){system.out.println ("chooseFlag=false "); } } } } public void setChooseFlag(boolean chooseFlag) { this.chooseFlag = chooseFlag; }}Copy the code
public class App { public static void main( String[] args ) throws InterruptedException { DeadThread t = new DeadThread(); Thread thread1 = new Thread(t); thread1.start(); Thread.sleep(100); // Wait for the second thread to start and get the lock1. Thread thread2 = new Thread(t); thread2.start(); }}Copy the code

Running the main method produces the following output:

ChooseFlag =false acquires lock lock2 chooseFlag=true acquires lock lock1Copy the code

Find the PID of the mian method

C:\Users\xxx\Desktop\test>jps
10432 RemoteMavenServer
10848
1012 App
12900 Jps
12632 Launcher

C:\Users\xxx\Desktop\test>jstack -l 1012 >> threadDump.txt

Copy the code

A deadlock was detected in the threaddump. TXT section

Found one Java-level deadlock:
=============================
"Thread-2":
  waiting to lock monitor 0x000000001cf21298 (object 0x000000076ba891e0, a java.lang.Object),
  which is held by "Thread-1"
"Thread-1":
  waiting to lock monitor 0x000000001cf1ea08 (object 0x000000076ba891d0, a java.lang.Object),
  which is held by "Thread-2"

Java stack information for the threads listed above:
===================================================
"Thread-2":
	at org.example.DeadThread.run(DeadThread.java:20)
	- waiting to lock <0x000000076ba891e0> (a java.lang.Object)
	- locked <0x000000076ba891d0> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:748)
"Thread-1":
	at org.example.DeadThread.run(DeadThread.java:32)
	- waiting to lock <0x000000076ba891d0> (a java.lang.Object)
	- locked <0x000000076ba891e0> (a java.lang.Object)
	at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.
Copy the code

Resolve deadlock issues

1. When a thread acquires a lock, it adds a time limit after which the thread will not wait for the lock

2. Banker algorithms

Interthread communication

Wait notification mechanism

synchronized + wait()/wait(long) + notify()/notifyAll()

Wait ()/wait(long) - in Object classes - can only be used after a lock has been acquired (within the scope of synchronized modifications), otherwise exceptions will occur. When a thread is notified (notify/notifyAll), the blocking thread acquires the lock and executes a wait. If the thread is waiting, the interrupt method on the thread object raises an interrupt exception. Notify ()/notifyAll() - in Object - is used only when a lock is acquired (within the scope of synchronized modification), otherwise an exception occurs - wakes up one or all threads and makes them enter the ready queue. The lock is not released. Note that locks are released only after synchronization code has been executed or an exception has been run in the synchronization codeCopy the code

Typical practice of waiting notification mechanism – producer – consumer model

One producer and one consumer

package org.example; public class MyThread3 { static class Util{ private String food; Object lock = new Object(); public void set(String value){ try { synchronized (lock) { if (food ! = null) {system.out.println (thread.currentThread ().getName()+"; lock.wait(); / / to use lock the object of this method, or even within the synchronized code, will still be at IllegalMonitorStateException} System. Out.println (Thread. The currentThread (). The getName () + ": + value); food = value; lock.notify(); / / to use lock the object of this method, or even within the synchronized code, will still be at IllegalMonitorStateException}} catch InterruptedException (e) {e.p rintStackTrace (); } } public void remove(){ try { synchronized (lock) { if (food == null) { System.out.println(thread.currentThread ().getName()+" : no food, customer waiting "); lock.wait(); } system.out.println (thread.currentThread ().getName()+"; food = null; lock.notify(); } }catch (InterruptedException e) { e.printStackTrace(); } } } static class ProducerThread extends Thread{ private Util util; public ProducerThread(String name, Util util){ super(name); this.util = util; } @override public void run() {while (true) {util. Set (); } } } static class ConsumerThread extends Thread{ private Util util; public ConsumerThread(String name, Util util){ super(name); this.util = util; } @Override public void run() { while (true) { util.remove(); } } } public static void main(String[] args) { Util util = new Util(); new ProducerThread("p-1", util).start(); new ConsumerThread("c-1",util).start(); }}Copy the code

The output

P-1: The chef is ready: Mahan's full set P-1: There are more dishes at the bar and the chef is waiting FOR them C-1: Customers are finished with their dishes at the bar and the chef is waiting for them p-1: The chef is ready with their dishes at the bar and the chef is waiting for them: Mahan set C-1: The bar is running out of food, customers are waiting...Copy the code

Multiple producers and consumers

How does procedural suspension occur and how can it be avoided

1) Util replaces “if” with “while”; 2) mian (” producer “and” consumer”

package org.example; public class MyThread4 { static class Util{ private String food; Object lock = new Object(); public void set(String value){ try { synchronized (lock) { while (food ! = null) {system.out.println (thread.currentThread ().getName()+"; lock.wait(); / / to use lock the object of this method, or even within the synchronized code, will still be at IllegalMonitorStateException} System. Out.println (Thread. The currentThread (). The getName () + ": + value); food = value; lock.notify(); / / to use lock the object of this method, or even within the synchronized code, will still be at IllegalMonitorStateException}} catch InterruptedException (e) {e.p rintStackTrace (); } } public void remove(){ try { synchronized (lock) { while (food == null) { System.out.println(thread.currentThread ().getName()+" : no food, customer waiting "); lock.wait(); } system.out.println (thread.currentThread ().getName()+"; food = null; lock.notify(); } }catch (InterruptedException e) { e.printStackTrace(); } } } static class ProducerThread extends Thread{ private Util util; public ProducerThread(String name, Util util){ super(name); this.util = util; } @override public void run() {while (true) {util. Set (); } } } static class ConsumerThread extends Thread{ private Util util; public ConsumerThread(String name, Util util){ super(name); this.util = util; } @Override public void run() { while (true) { util.remove(); } } } public static void main(String[] args) throws InterruptedException { Util util = new Util(); for (int i=1; i<3; i++) { new ProducerThread("p-"+i, util).start(); } for (int i=1; i<3; i++) { new ConsumerThread("c-"+i, util).start(); } Thread.sleep(10000); Int threadCount = thread.CurrentThread ().getThreadGroup().Activecount (); Thread[] threads = new Thread[threadCount]; Thread.currentThread().getThreadGroup().enumerate(threads); for (Thread t: threads) { System.out.println(t.getName()+" "+t.getState()); }}}Copy the code

The output

P-1: Chef is ready: Mahan's full set P-1: There are more dishes at the bar, chef is waiting P-2: There are more dishes at the bar, chef is waiting C-1: Customers are finished with their dishes at the bar: Mahan's full set C-1: Chef is ready: Mahan's full set P-1: C-2: Customers are finished with their dishes: Mahan set Menu C-2: Customers are out of dishes at the bar, customers are waiting for C-1: Main RUNNABLE Monitor Ctrl-break RUNNABLE P-1 WAITING P-2 WAITING C-1 WAITING C-2 WAITINGCopy the code

All threads are in a WAITING state and cannot run. The reason for suspended animation is that when a thread wakes up with notify, it only wakes up one thread in the blocking queue. If the thread wakes up the producer, it will block the producer.

NotifyAll is used instead of notify to wake up all threads.

Pipeline flow message

Pipe flows are used to transfer data directly between threads (without terminating temporary files, for example). One thread sends data to the output pipe, and another thread reads data from the input pipe.

Pipe stream: PipedInputStream & PipedOutputStream Pipe stream: PipedWriter & PipedReaderCopy the code

Example of byte stream, similar to character stream

package org.example; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; public class MyThread5 { static class Util{ public void writeData(PipedOutputStream pipedOutputStream, String content){ try { pipedOutputStream.write(content.getBytes()); } catch (IOException e) { e.printStackTrace(); }finally { if (pipedOutputStream ! = null){ try { pipedOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } public String readData(PipedInputStream pipedInputStream){ StringBuilder stringBuilder = new StringBuilder(); try { byte[] arr = new byte[1024]; int length; while ((length = pipedInputStream.read(arr)) ! = -1) { stringBuilder.append(new String(arr, 0, length)); } }catch (IOException e){ e.printStackTrace(); }finally { if (pipedInputStream ! = null){ try { pipedInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return stringBuilder.toString(); } } static class WriteThread extends Thread{ private Util util; private PipedOutputStream pipedOutputStream; public WriteThread(String name, Util util, PipedOutputStream pipedOutputStream){ super(name); this.util = util; this.pipedOutputStream = pipedOutputStream; } @override public void run() {String MSG = ""; System.out.println(thread.currentThread ().getName()+" "+ MSG); util.writeData(pipedOutputStream, msg); } } static class ReadThread extends Thread{ private Util util; private PipedInputStream pipedInputStream; public ReadThread(String name, Util util, PipedInputStream pipedInputStream){ super(name); this.util = util; this.pipedInputStream = pipedInputStream; } @Override public void run() { String data = util.readData(pipedInputStream); System.out.println(thread.currentThread ().getName()+" "+ data); } } public static void main(String[] args) throws IOException, InterruptedException { Util util = new Util(); PipedInputStream = new PipedInputStream(); PipedOutputStream pipedOutputStream = new PipedOutputStream(); pipedInputStream.connect(pipedOutputStream); new WriteThread("p-1", util, pipedOutputStream).start(); Thread.sleep(1000); New ReadThread("c-1", util, pipedInputStream).start(); }}Copy the code

The output

P-1 Data written to: magnitude safin fang Sark sends yellow van der SAR to a third party C-1 Data read: magnitude Safin Fang Sark sends yellow van der SAR to a third partyCopy the code

join

Usage scenarios

In many cases, the main thread creates and starts the child thread, and if the child thread does a lot of time-consuming computation, the main thread will often end before the child thread does. If the main thread wants to wait until the child thread completes, for example, processing a piece of data, the main thread retrieves the value in that data. This is done through the Join method.

Note that the join()/ Join (long) methods are implemented using the wait method, which is synchronous, as shown in the source pseudocode below. Because wait raises an exception when a thread is interrupted, JOIN also raises an exception when a thread is interrupted.

 public final synchronized void join(long millis)
    throws InterruptedException {
     //...
     wait
     //...
 }
Copy the code

Thread private data

In addition to making local variables naturally thread private, there is another way to make thread private: ThreadLocal&InheritableThreadLocal.

ThreadLocal

Each thread can bind its own value to ThreadLocal, and that value is thread-private and visible throughout the current thread.

ThreadLocal stores private variables that are not inheritable, meaning that variables stored in the parent thread are not visible in the child thread, and vice versa. Each parent thread has its own value.

InheritableThreadLocal

Each thread can bind its own value to InheritableThreadLocal, and that value is thread-private and visible throughout the current thread and child threads.

InheritableThreadLocal stores private variables that are inheritable. At the same time, the value can be inherited and then modified.

Value inheritance and modification is implemented by customizing ThreadLocal and overriding the following methods of the parent class:

protected T childValue(T parentValue) {
        return parentValue;
    }
Copy the code

Such as MyThreadLocal class

Protected T childValue(T parentValue) {return "childValue, "+parentValue; }Copy the code

Each time a value is obtained by get, the parent thread is preceded by “I am the child thread,” and returns the value

The Timer Timer

Commonly used API

Schedule (TimerTask task, long delay) Schedule (TimerTask task, Date time) 2. Schedule (TimerTask task, long delay, long period) Schedule (TimerTask task, Date firstTime, long period) Periodic execution scheduleAtFixedRate(TimerTask task, long delay, long period) scheduleAtFixedRate(TimerTask task, Date firstTime, long period)Copy the code

Features and differences

Features and differences between periodic execution schedule and scheduleAtFixedRate

  • If the time of the scheduled task is not delayed, the time of the next task is the time when the last task starts +period. If the execution time of a task is delayed, the next task is executed at the time when the last task ends

  • ScheduleAtFixedRate The time when the next task is executed is the time when the last task ends, no matter that the time of the task is not delayed

  • ScheduleAtFixedRate has the feature of task catch-up. Schedule does not have. If a task is specified to start at a time earlier than the time when the task is first executed, the schedule ignores the tasks that are scheduled to start during this time. ScheduleAtFixedRate supplements the execution.

  • Schedule (TimerTask task, Date firstTime, long Period) and scheduleAtFixedRate(TimerTask task, Date firstTime, Long period) if firstTime is specified earlier than the current time, it will be executed immediately