preface

Thread objects are objects that can produce threads. For example, in the Java platform, Thread objects, Runnable objects. A thread is a sequence of Pointers that is executing. On the Java platform, this refers to a relatively independent process in the body of the run method, starting with start() on a thread object.

Compared with multi-process, multi-thread has the following advantages:

(1) Processes cannot share data, threads can;

(2) The system needs to reallocate system resources for the process to create a process, so the cost of creating a thread is relatively small;

(3) Java language built-in multithreading function support, simplify Java multithreading programming.

Summary of a multi-threaded knowledge graph, can be shared with you:

Create thread and start

(1) Create Thread class by inheriting Thread class

The steps and code for creating a Thread class by inheriting the Thread class are as follows:

• Define a subclass that inherits Thread and override its run() method;

• Create an instance of Thread subclass, that is, create a Thread object;

• Call the start() method of the thread object to start the thread.

class SomeThead extends Thraad { public void run() { //do something here } } public static void main(String[] args){ SomeThread oneThread = new SomeThread(); Step 3: Start the thread: onethread.start (); }Copy the code

(2) implement the Runnable interface to create thread classes

The specific steps and code to create a thread class by implementing the Runnable interface are as follows:

• Define an implementation class for the Runnable interface and override its run() method;

• Create an instance of the Runnable implementation class and use it as the target object for Thread, which is the actual Thread object.

class SomeRunnable implements Runnable   { 
  public void run()   { 
  //do something here  
  }  
} 
Runnable oneRunnable = new SomeRunnable();   
Thread oneThread = new Thread(oneRunnable);   
oneThread.start(); 
Copy the code

(3) Create threads with Callable and Future

The steps and code for creating threads with Callable and Future are as follows:

• Create an implementation class for the Callable interface and implement a call() method that will act as a thread body and have a return value. • Create an instance of the Callable implementation class that wraps the Callable object with a FutureTask class that wraps the return value of the Callable object’s call() method. • Create and start a new Thread using the FutureTask object as the target of the Thread object. • Call FutureTask’s get() method to get the return value after the child thread finishes executing. The Callable interface (also a single method) is defined as follows:

public interface Callable { V call() throws Exception; } Step 1: create a class SomeCallable(omitted) that implements the Callable interface. Step 2: Create a class object: Callable oneCallable = new SomeCallable(); Step 3: Create a FutureTask object from Callable: FutureTask oneTask = new FutureTask(oneCallable); Note: FutureTask is a wrapper that is created by accepting a Callable and implements both the Future and Runnable interfaces. Step 4: Create a Thread object from FutureTask: Thread oneThread = new Thread(oneTask); Step 5: Start the thread: onethread.start ();Copy the code

Second, the life cycle of threads

1. Create a state

When a Thread object is created with the new keyword and the Thread class or its subclasses, the Thread object is in a newborn state. A thread in a newborn state has its own memory space and enters a runnable state by calling the start method.

Note: not the threads are started again call start () method, otherwise there will be a Java lang. IllegalThreadStateException anomalies.

2. Ready state

A thread in the ready state that is ready to run, but not yet allocated to a CPU, is in a thread-ready queue (albeit in the form of a queue, in fact, called a runnable pool rather than a runnable queue). Because CPU scheduling is not necessarily in first-in, first-out order), waiting for the system to allocate cpus to it. The waiting state is not the execution state. When the system selects a Thread object to be executed, it changes from the waiting state to the execution state. The action selected by the system is called “CPU scheduling”. Once the CPU is acquired, the thread enters the running state and automatically calls its own run method.

Tip: If you want the child Thread to execute immediately after calling the start() method, you can use thread.sleep () to block the main Thread and move the child Thread.

3. Running status

The running thread is the most complex, and can be blocked, ready, or dead.

A thread in the ready state, if scheduled by the CPU, changes from the ready state to the run state to perform tasks in the run() method. If the thread loses CPU resources, it goes from the running state to the ready state again. Wait for the system to allocate resources again. You can also call yield() on a running thread, which will yield CPU resources and become ready again.

Note: A thread changes from a running state to a blocked state when:

The thread calls a blocking IO method, which is blocked before the method returns. The thread attempts to obtain a synchronization monitor. But the change synchronization monitor is being held by another thread (4). The thread is waiting for a notify (5). The program calls the thread's suspend method to suspend the thread. However, this method is prone to deadlocks, so programs should avoid using it.Copy the code

A thread changes from a running state to a dead state when its run() method finishes executing, or when it is terminated by force, such as an exception, or when a stop(), desyory() method is called, etc.

4. Blocked state

In some cases, a running thread that executes a sleep method, or waits for a resource such as an I/O device, gives up the CPU and temporarily stops running itself, entering a blocked state.

A thread in the blocked state cannot enter the ready queue. Only when the cause of the blocking is eliminated, such as when it is time to sleep, or when the waiting I/O device is idle, will the thread go to the ready state, queue up again in the ready queue, and resume running from where it left off after being selected by the system. There are three ways to pause Threads execution:

5. Death

A thread is considered dead when its run() method finishes executing, or when it is forced to terminate. The thread object may be alive, but it is no longer a single thread executing. Once a thread dies, it cannot be resurrected. If on a dead thread calls the start () method, which will be thrown. Java lang. IllegalThreadStateException anomalies.

Third, thread management

Java provides some convenient methods for controlling thread state. Details are as follows:

1. Thread sleep — sleep

If we need to suspend the currently executing Thread for a while and block, we can call Thread’s sleep method.

Note:

(1) Sleep is a static method, and it is best not to call it with an instance of Thread, because it always sleeps with the currently running Thread, not the calling Thread object, and it only works with the running Thread object. Here’s an example:

public class Test1 { public static void main(String[] args) throws InterruptedException { System.out.println(Thread.currentThread().getName()); MyThread myThread=new MyThread(); myThread.start(); myThread.sleep(1000); Thread.sleep(10); thread.sleep (10); for(int i=0; i<100; i++){ System.out.println("main"+i); }}}Copy the code

(2) Java thread scheduling is the core of Java multithreading. Only good scheduling can give full play to the performance of the system and improve the execution efficiency of the program. However, no matter how programmers write schedules, they can only affect the order of thread execution to the maximum extent, but cannot achieve precise control. After using the sleep method, the Thread will enter the blocked state, and only when the sleep time ends, it will re-enter the ready state, and the ready state will enter the running state, which is controlled by the system, so it is impossible to accurately interfere with it. Therefore, if Thread. Sleep (1000) is called to make the Thread sleep for 1 second, It might be more than 1 second.

2. Thread yields — yield

The yield() method is similar to the sleep() method in that it is also a static method provided by the Thread class. It can also suspend the currently executing Thread, freeing CPU resources for another Thread. Unlike the sleep() method, however, it does not go into the blocking state, but into the ready state. The yield() method simply pauses the current thread and re-enters the ready thread pool, allowing the system’s thread scheduler to re-schedule it. It is entirely possible that when a thread calls the yield() method, the thread scheduler will re-schedule it for execution.

In fact, when a thread suspends with yield(), ready threads with the same or higher priority than the current thread are more likely to get the chance to execute. Just maybe, of course, because we can’t precisely interfere with the CPU scheduling threads. Usage:

Public class Test1 {public static void main(String[] args) throws InterruptedException {new MyThread(" low ", 1).start(); New MyThread(" intermediate ", 5).start(); New MyThread(" advanced ", 10).start(); } } class MyThread extends Thread { public MyThread(String name, int pro) { super(name); // Set the thread name this.setPriority(pro); } @override public void run() {for (int I = 0; i < 30; I ++) {system.out.println (this.getName() + "thread" + I + "! ); if (i % 5 == 0) Thread.yield(); }}}Copy the code

Note: The difference between the sleep() method and yield() method is as follows:

(1) After the sleep method pauses the current thread, it will enter the blocking state, and only when the sleep time is up, it will enter the ready state. When the yield method is called, it goes directly to the ready state, so it is possible to get into the ready state and then be scheduled to the run state.

The sleep method declaration throws InterruptedException, so call the sleep method either to catch the exception or to display a declaration that throws it. The yield method does not declare to throw a task exception.

The sleep method is more portable than the yield method, and generally should not be relied on to control the execution of concurrent threads.

Thread merge — join

Thread merging means combining threads from several parallel threads into a single Thread. When a Thread must wait for another Thread to complete its execution, the Thread class provides a join method to do this. Note that this method is not static.

As you can see from the list of methods above, it has three overloaded methods:

Void join() The current thread waits for the thread to terminate. Void Join (long millis) The maximum time that the current thread waits for this thread to terminate is millis milliseconds. Void JOIN (long millis,int nanos) void join(long millis,int nanos)Copy the code

The maximum waiting time for this thread to terminate is Millis milliseconds + Nanos nanoseconds. If the thread does not complete within millis time, the current thread enters the ready state and waits for the CPU to schedule again

4. Set the thread priority

Each thread has a priority attribute when it executes, and threads with higher priority get more execution opportunities, while threads with lower priority get less execution opportunities. Like thread sleep, thread priority still does not guarantee thread execution order. However, threads with higher priority are more likely to acquire CPU resources, and threads with lower priority are not deprived of execution.

By default, each thread has the same priority as the parent thread that created it, and by default, the main thread has normal priority.

Note: The Thread class provides setPriority(int newPriority) and getPriority() methods to set and return the priority of a specified Thread. The setPriority method takes an integer ranging from 1 to ·0. You can also use the three static constants provided by the Thread class:

MAX_PRIORITY   =10

MIN_PRIORITY   =1

NORM_PRIORITY   =5
Copy the code
Public class Test1 {public static void main(String[] args) throws InterruptedException {new MyThread(" high ", 10).start(); New MyThread(" low ", 1).start(); } } class MyThread extends Thread { public MyThread(String name,int pro) { super(name); // Set the thread name setPriority(pro); } @override public void run() {for (int I = 0; i < 100; I ++) {system.out.println (this.getName() + "thread" + I + "! ); }}}Copy the code

Note: Although Java provides 10 priority levels, these priority levels require operating system support. Different operating systems have different priorities and do not correspond well to Java’s 10 priorities. The three static constants MAX_PRIORITY, MIN_PRIORITY, and NORM_PRIORITY should be used to set priorities for maximum portability.

5. Background (daemon) threads

Daemons are used less often, but not useless; for example, JVM threads for garbage collection, memory management, and so on are daemons. There is also in the database application, the use of the database connection pool, connection pool itself also contains a lot of background threads, monitoring the number of connections, timeout, status and so on. Call the thread object’s method setDaemon(true) to set it as a daemon thread. Daemon threads are used to perform background tasks, such as playing background music while your application is running, automatic syntax checking, automatic saving in the text editor, etc.

• Java garbage collection is also a daemon thread. The nice thing about daemons is that you don't have to worry about ending them. For example, if you want to play background music while your application is running, if you set the thread that plays background music as a non-daemon thread, you will not only exit the main thread, but also notify the thread that plays background music to exit when the user requests to exit. This is not required if you set it to a daemon thread.Copy the code

SetDaemon method:

Public final void setDaemon(Boolean on) Marks this thread as a daemon or user thread. The Java virtual machine exits when all running threads are daemons. This method must be called before starting the thread. The method first calls the thread's checkAccess method, taking no arguments. This may throw a SecurityException (in the current thread). Parameter: on - If true, marks the thread as a daemon thread. Throws: IllegalThreadStateException - if this thread is active. SecurityException - If the current thread cannot modify the thread.Copy the code

Note: the standard of the JRE to determine whether the program has finished execution is that all foreground threads have finished, regardless of the status of the background thread. Therefore, be aware of this problem when using the background county.

End the thread correctly

Thread. Stop (), Thread. Suspend, Thread, resume, Runtime. RunFinalizersOnExit these terminated threads running method has been abandoned, use them is extremely unsafe! To terminate a thread safely and efficiently, use the following methods:

• Execute the run method normally, then terminate; • Control loop conditions and judge condition identifiers to terminate the thread.Copy the code
class MyThread extends Thread { int i=0; boolean next=true; @Override public void run() { while (next) { if(i==10) next=false; i++; System.out.println(i); }}}Copy the code

4. Thread synchronization

Java allows multithreading concurrent control, when multiple threads at the same time operating a resources can be Shared variables (such as data to add and delete), will lead to inaccurate data, the conflict between each other, so join the synchronization locks in order to avoid in this thread did not complete before operation, and the other threads calls, to ensure the uniqueness of the variables and the accuracy.Copy the code

1. Synchronization method

That is, methods modified by the synchronized keyword. Because every object in Java has a built-in lock, the built-in lock protects the entire method when you decorate it with this keyword. The built-in lock needs to be acquired before the method can be called, otherwise it is blocked.

public synchronized void save(){}

Note: The synchronized keyword can also modify a static method that, if called, locks the entire class

2. Synchronize code blocks

A block of statements decorated with the synchronized keyword. Blocks decorated with this keyword are automatically synchronized with built-in locks.

   public class Bank {  
     
        private int count =0;//账户余额  
     
        //存钱  
        public   void addMoney(int money){  
     
            synchronized (this) {  
                count +=money;  
            }  
            System.out.println(System.currentTimeMillis()+"存进:"+money);  
        }  
     
        //取钱  
        public   void subMoney(int money){  
     
            synchronized (this) {  
                if(count-money < 0){  
                    System.out.println("余额不足");  
                    return;  
                }  
                count -=money;  
            }  
            System.out.println(+System.currentTimeMillis()+"取出:"+money);  
        }  
     
        //查询  
        public void lookMoney(){  
            System.out.println("账户余额:"+count);  
        } 
    }
Copy the code

Note: Synchronization is an expensive operation, so you should minimize the amount of synchronization. It is often not necessary to synchronize the entire method; you can use a synchronized code block to synchronize key code.

3. Use volatile to synchronize threads

• The volatile keyword provides a lock-free mechanism for domain variable access;

• Using volatile to decorate a field tells the virtual machine that the field may be updated by another thread;

• Recalculate each time the field is used instead of using the value in the register;

• Volatile does not provide any atomic operations, nor can it be used to modify variables of final type.

public class SynchronizedThread { class Bank { private volatile int account = 100; public int getAccount() { return account; } @param money */ public synchronized void save(int money) {account += money; } @param money */ public void save1(int money) {synchronized (this) {account += money; } } } class NewThread implements Runnable { private Bank bank; public NewThread(Bank bank) { this.bank = bank; } @Override public void run() { for (int i = 0; i < 10; i++) { // bank.save1(10); bank.save(10); System.out.println(I + "" +bank.getAccount()); Public void useThread() {Bank Bank = new Bank(); NewThread new_thread = new NewThread(bank); System. The out. Println (" thread 1 "); Thread thread1 = new Thread(new_thread); thread1.start(); System. The out. Println (" thread 2 "); Thread thread2 = new Thread(new_thread); thread2.start(); } public static void main(String[] args) { SynchronizedThread st = new SynchronizedThread(); st.useThread(); }Copy the code

Note: Asynchrony problems in multithreading occur mainly in reading and writing to the domain. If you let the domain itself avoid this problem, you do not need to modify the way you operate on the domain. With final, lock-protected, and volatile domains, the problem of non-synchronization can be avoided.

4. Use reentrant locks to synchronize threads

A java.util.concurrent package has been added in JavaSE5.0 to support synchronization. The ReentrantLock class is a reentrant, mutually exclusive Lock that implements the Lock interface and has the same basic behavior and semantics as using synchronized methods and fast, and extends its capabilities. Common methods of the ReenreantLock class are:

ReentrantLock() : creates an instance of ReentrantLock lock() : obtains the lock Unlock () : releases the lockCopy the code

Note: ReentrantLock() also has a constructor that can create fair locks, but it is not recommended because it can significantly reduce program performance

Class Bank {private int account = 100; Private Lock Lock = new ReentrantLock(); public int getAccount() { return account; Synchronized public void save(int money) {lock. Lock (); try{ account += money; }finally{ lock.unlock(); }}}Copy the code

5. Thread communication

1. Communicate with Object class wait(), notify() and notifyAll()

After executing wait(), the thread is suspended.

When a thread is running, a thread pool is created in memory, and the frozen thread exists in the thread pool. When notify() is executed, the thread in the pool is also awakened. If there are multiple threads in the pool, the first frozen thread is awakened.

Notifyall (), wakes up all threads in the thread pool.

Note: (1) Wait (),notify (), and notifyall() are all used in synchronization because they operate on the thread holding the lock.

(2) Wait (),notify(), and notifyall() must be used to identify the lock held by the thread on which they operate, because the wait and wake must be threads under the same lock; The lock can be any Object, so all three methods are methods in the Object class.

Examples of individual consumer producers are as follows:

Class Resource{// Private String name; private int count=1; private boolean flag=false; public synchronized void set(String name){ if(flag) try{wait(); }catch(Exception e){} this.name=name+"---"+count++; System.out.println(Thread.currentThread().getName()+"... Producers..." +this.name); flag=true; this.notify(); } public synchronized void out(){ if(! flag) try{wait(); }catch(Exception e){} System.out.println(Thread.currentThread().getName()+"... Consumers..." +this.name); flag=false; this.notify(); } } class Producer implements Runnable{ private Resource res; Producer(Resource res){ this.res=res; } public void run(){while(true){res.set(" merchandise "); } } } class Consumer implements Runnable{ private Resource res; Consumer(Resource res){ this.res=res; } public void run(){ while(true){ res.out(); } } } public class ProducerConsumerDemo{ public static void main(String[] args){ Resource r=new Resource(); Producer pro=new Producer(r); Consumer con=new Consumer(r); Thread t1=new Thread(pro); Thread t2=new Thread(con); t1.start(); t2.start(); }}// The result is normal, the producer produces a good, followed by the consumer consumes a good.Copy the code

But if there are multiple producers and multiple consumers, the code above is problematic, for example, 2 producers, 2 consumers, the result may be that one good produced once is consumed twice, or two goods produced consecutively and only one is consumed, This is because there are four threads operating on Resource r, and notify() awakens the first wait() thread in the pool. The producer thread wakes up from wait() and instead of judging the flag, runs straight down to print a new item, thus producing two items in a row.

To avoid this, modify the code as follows:

class Resource{ private String name; private int count=1; private boolean flag=false; Public synchronized void set(String name){while(flag) /* Synchronized void set(String name){while(flag) */ try{wait(); }catch(Exception e){} this.name=name+"---"+count++; System.out.println(Thread.currentThread().getName()+"... Producers..." +this.name); flag=true; this.notifyAll(); /* Which used to be notity(), now is changed to notifyAll(), which allows the producer thread to wake up the waiting consumer thread after producing an item. Otherwise, if the producer thread is changed to while, all producers and consumers may wait(). */ } public synchronized void out(){ while(! */ try{wait(); */ try{wait(); }catch(Exception e){} System.out.println(Thread.currentThread().getName()+"... Consumers..." +this.name); flag=false; this.notifyAll(); /* Which used to be notity(), now changes to notifyAll(), which allows the producer thread to wake up after the consumer thread consumes an item. Otherwise, when changing to while, all producers and consumers may wait(). */ } } public class ProducerConsumerDemo{ public static void main(String[] args){ Resource r=new Resource(); Producer pro=new Producer(r); Consumer con=new Consumer(r); Thread t1=new Thread(pro); Thread t2=new Thread(con); Thread t3=new Thread(pro); Thread t4=new Thread(con); t1.start(); t2.start(); t3.start(); t4.start(); }}Copy the code

2. Use Condition to control thread communication

Multi-threaded upgrade solutions are provided in JDK1.5:

(1) Replace synchronized with explicit Lock;

(2) Replace Object’s wait(), notify(), and notifyAll() with Condition objects, which can be obtained through Lock Lock objects;

(3) Multiple Condition objects can be bound to a Lock object, so that the local thread can only wake up the other thread, but before JDK1.5, a synchronization can only have one Lock, different synchronization can only be distinguished by locks, and locks are easy to deadlock when nested.

class Resource{ private String name; private int count=1; private boolean flag=false; private Lock lock = new ReentrantLock(); /*Lock is an interface and ReentrantLock is a direct subclass of that interface. */ private Condition condition_pro=lock.newCondition(); Private Condition condition_con=lock.newCondition(); Public void set(String name){lock.lock(); // Lock the code between this statement and lock.unlock(). Try {while(flag) condition_pro.await(); // producer thread waits on conndition_pro object this.name=name+"-- "+count++; System.out.println(Thread.currentThread().getName()+"... Producers..." +this.name); flag=true; condition_con.signalAll(); } finally{ lock.unlock(); // Unlock () goes in the finally block. } } public void out(){ lock.lock(); // Lock the code between this statement and locke.unlock (). Try {while(! flag) condition_con.await(); // The consumer Thread waits for system.out.println (thread.currentThread ().getName()+"... Consumers..." +this.name); flag=false; condition_pro.signqlAll(); Condition_pro */ / finally{lock.unlock(); }}}Copy the code

Use BlockingQueue to control thread communication

BlockingQueue is an interface and a subinterface of the Queue. BlockingQueue has a feature that blocks when a producer thread tries to put an element in the BlockingQueue if the queue is full. But when the consumer thread tries to fetch an element from BlockingQueue, it blocks if the queue is empty. The two threads of the program can control thread communication by alternately putting and taking elements out of the BlockingQueue.

BlockingQueue provides two methods to support blocking:

(1) Put (E E) : Try to put the Eu element in BlockingQueue. If the queue is full, block the thread.

(2) Take () : Attempts to fetch an element from the head of BlockingQueue, and blocks the thread if the queue is empty.

BlockingQueue inherits from the Queue interface, but can also use methods from the Queue interface, which can be grouped into three groups:

(1) Insert elements at the end of the queue, including add (E E), Offer (E E), put (E E) methods. When the queue is full, these three methods will throw an exception, return false, and block the queue, respectively.

(2) Delete at the head of the queue and return the deleted element. These include the remove (), poll (), and take () methods, which throw an exception, return false, and block when the queue is empty, respectively.

(3) Fetch but not delete elements at the head of the queue. These include the Element () and peek () methods, which throw an exception and return false, respectively, when the queue is empty.

The BlockingQueue interface contains the following five implementation classes:

ArrayBlockingQueue: Array-based implementation of BlockingQueue queue. LinkedBlockingQueue: BlockingQueue based on a linked list implementation. PriorityBlockingQueue: This is not a guaranteed blocking queue. When this queue calls remove (), poll (), take (), etc., it does not fetch the longest element in the queue, but the smallest element in the queue. It determines the size of elements either by naturally sorting them based on their size (which implements the Comparable interface) or by custom sorting using the Comparator. SynchronousQueue: SynchronousQueue. The save and fetch operations on the queue must be performed alternately. DelayQueue: It is a special BlockingQueue, which is based on PriorityBlockingQueue, but DelayQueue requires that all collection elements implement the Delay interface (which has only one long getDelay () method). DelayQueue is sorted based on the return value of the getDalay () method of the collection element.Copy the code

An example of copy:

1 import java.util.concurrent.ArrayBlockingQueue; 2 import java.util.concurrent.BlockingQueue; 3 public class BlockingQueueTest{4 public static void main(String[] args)throws Exception{5 // Create an BlockingQueue 6 with capacity 1  7 BlockingQueue<String> b=new ArrayBlockingQueue<>(1); 9 new Producer(b).start(); 10 new Producer(b).start(); 11 new Producer(b).start(); 13 new Consumer(b).start(); 14 15 } 16 } 17 class Producer extends Thread{ 18 private BlockingQueue<String> b; 19 20 public Producer(BlockingQueue<String> b){ 21 this.b=b; 22 23 } 24 public synchronized void run(){ 25 String [] str=new String[]{ 26 "java", 27 "struts", 28 "Spring" 29 }; 30 for(int i=0; i<9999999; I++){31 system.out.println (getName()+" producer ready to produce collection elements!" ); 32 try{ 33 34 b.put(str[i%3]); 35 sleep(1000); 37 38}catch(Exception e){system.out.println (e); } 39 system.out.println (getName()+" production done: "+b); 40 } 41 42 } 43 } 44 class Consumer extends Thread{ 45 private BlockingQueue<String> b; 46 public Consumer(BlockingQueue<String> b){ 47 this.b=b; 48} 49 public synchronized void run(){50 51 while(true){52 system.out.println ()+" ); 53 try{ 54 sleep(1000); 55 // Thread is blocked if queue is empty. 56 b.stack (); 57 }catch(Exception e){System.out.println(e); } 58 system.out.println (getName()+" +b "); 59} 60 61} 62}Copy the code

Thread pools

There are three benefits to using thread pools properly.

Reduce resource consumption. Reduce the cost of thread creation and destruction by reusing created threads. Improve response speed. When a task arrives, it can be executed immediately without waiting for the thread to be created. Improve thread manageability. Threads are scarce resources. If they are created without limit, they will not only consume system resources, but also reduce system stability. Thread pools can be used for unified allocation, tuning, and monitoring.

1. Use Executors factory to generate a thread pool

The greatest advantage of the Executor thread pool framework is the decoupling of task submission and execution. The tasks that the client is about to perform are encapsulated into tasks and then submitted. How the Task executes the client is transparent. Specifically, submitting a Callable object to the ExecutorService (such as ThreadPoolExecutor, the most common thread pool) will result in a Future object, whose GET method is called to wait for the execution result. The class structure diagram of thread pool implementation principle is as follows:

All the classes involved in the internal implementation of thread pools in the figure above do not help us understand how thread pools are used. Starting with the client side, let’s take a look at the class structure involved in the client using thread pools:

ExecutorService is an interface to the thread pool defined in Java, which is provided in the java.util.concurrent package. The Java API provides two implementations of the ExecutorService interface, and the Java thread pool implementation classes are as follows:

ThreadPoolExecutor
 
 ScheduledThreadPoolExecutor
Copy the code

ExecutorService also inherits the Executor interface (or exector factory), which has only one execute() method.

Perform the following steps to execute a multithreaded task using Executors:

• Call the Static factory method of the Executors class to create an ExecutorService object that represents a thread pool;

• Create an instance of a Runnable implementation class or a Callable implementation class to perform tasks as a thread;

• Call the Submit () method of the ExecutorService object to submit a Runnable instance or a Callable instance;

• when you do not want to commit tasks, call the shutdown() method of the ExecutorService object to shutdown the thread pool.

(1) Create a thread pool by using the Static Factory class Executors as follows:

1, newFixedThreadPool () :

This method returns a thread pool with a fixed number of threads. The number of threads in the pool is always the same. No new threads are created, and no existing threads are destroyed. Chestnut: if a new task is submitted, if there is a free thread in the thread pool, it will immediately use the free thread to process the task, if not, the new task will be put in a task queue, once there is a free thread, then according to FIFO processing task queue in the task.

2, newCachedThreadPool () :

Function: This method returns a thread pool that can adjust the number of threads in the pool as needed. That is, the number of threads in the thread pool is uncertain and is dynamically adjusted according to the actual situation. Chestnuts: if all threads in this thread pool was at work, at a time when a new task to submit, so will create a new thread for processing tasks, and if there are some threads to complete the task before, now have a new task to submit again, so will not create a new thread to handle, but reuse the idle thread to deal with new tasks. Now, if someone has a question, wouldn’t the pool have more and more threads? In fact, it does not, because all threads in the thread pool have a parameter of “hold active time”. By configuring this parameter, if the idle thread in the thread pool exceeds the “save active time”, the thread will stop immediately. The default “hold active time” of the thread pool is 60 seconds.

3, newSingleThreadExecutor () :

Function: This method returns a thread pool with only one thread, that is, only one thread can execute tasks at a time. The redundant tasks are stored in a task queue, waiting for this thread to be idle, and then execute tasks in the task queue in FIFO order.

4, newScheduledThreadPool () :

Function: This method returns a pool of threads that can be controlled to perform a task periodically or periodically.

5, newSingleThreadScheduledExecutor () :

Function: This method returns a pool of threads that can be controlled to perform a task periodically or periodically. The difference is that the thread pool size is 1, whereas the thread pool size can be specified.

Note: the Executors are just a factory class, it is all the method returns the ThreadPoolExecutor, ScheduledThreadPoolExecutor the two instances of the class.

(2) There are several ExecutorService execution methods:

  • execute(Runnable)
  • submit(Runnable)
  • submit(Callable)
  • invokeAny(…)
  • invokeAll(…)
execute(Runnable)

This method takes an instance of Runnable and executes it asynchronously, as shown in the following example:

ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.execute(new Runnable() { public void run() { System.out.println("Asynchronous task"); }}); executorService.shutdown();Copy the code
submit(Runnable)

Submit (Runnable) and execute(Runnable) return a Future object to check whether the submitted task has been executed.

Future future = executorService.submit(new Runnable() { public void run() { System.out.println("Asynchronous task"); }}); future.get(); //returns null if the task has finished correctly.Copy the code

Note: If the task completes, the future.get() method returns null. Note that the future.get() method blocks.

submit(Callable)

Submit (Callable) is similar to submit(Runnable) in that it returns a Future object, but otherwise receives an implementation of a Callable. The Call () method in the Callable interface returns a value, The run() method in the Runnable interface is void and has no return value. Take the following example:

Future future = executorService.submit(new Callable(){ public Object call() throws Exception { System.out.println("Asynchronous Callable"); return "Callable Result"; }}); System.out.println("future.get() = " + future.get());Copy the code

If the task completes, the future.get() method returns the result of the Callable task. In addition, the future.get() method blocks.

InvokeAny (…).

invokeAny(…) Method receives a collection of Callable tasks, and executing this method does not return a Future, but the result of one of the Callable tasks. This method also does not guarantee which task will be returned, but one or the other. Take the following example:

ExecutorService executorService = Executors.newSingleThreadExecutor(); Set<Callable<String>> callables = new HashSet<Callable<String>>(); callables.add(new Callable<String>() { public String call() throws Exception { return "Task 1"; }}); callables.add(new Callable<String>() { public String call() throws Exception { return "Task 2"; }}); callables.add(new Callable<String>() { public String call() throws Exception { return "Task 3"; }}); String result = executorService.invokeAny(callables); System.out.println("result = " + result); executorService.shutdown();Copy the code

If you try to execute the code above, it will return a result every time it is executed, and the result will change. It may return “Task2” or “Task1” or something else.

InvokeAll (…).

invokeAll(…) With invokeAny (…). Similarly, it receives a collection of Callable tasks, but returns a List of Future objects for each Callable task. Here is an example of the situation:

ExecutorService executorService = Executors.newSingleThreadExecutor(); Set<Callable<String>> callables = new HashSet<Callable<String>>(); callables.add(new Callable<String>() { public String call() throws Exception { return "Task 1"; }}); callables.add(new Callable<String>() { public String call() throws Exception { return "Task 2"; }}); callables.add(new Callable<String>() { public String call() throws Exception { return "Task 3"; }}); List<Future<String>> futures = executorService.invokeAll(callables); for(Future<String> future : futures){ System.out.println("future.get = " + future.get()); } executorService.shutdown();Copy the code
(3) ExecutorService shutdown method

We should close the ExecutorService when we are done with it, otherwise the threads inside it will stay running. For example, if the application is started using the main() method, the application will continue to run if the ExecutorService in the application is not closed after main() exits. This happens because the threads running in the ExecutorService prevent the JVM from shutting down.

To shutdown the threads executing within the ExecutorService, you can call the executorservice.shutdown () method. ExecutorService does not shutdown immediately after the shutdown() method is called, but it does not accept new tasks until all current threads have completed execution, and all tasks submitted before shutdown() are executed.

If you want to shut down the ExecutorService immediately, we can call the ExecutorService. ShutdownNow () method. This action skips all tasks in progress and submitted tasks that have not yet been executed. However, it does not guarantee that the tasks being performed will either stop or finish.

ForkJoinPool creates thread pools using Java8 enhanced ForkJoinPool

In Java 8, the concept of automatic parallelization was introduced. It enables a portion of Java code to be executed automatically in parallel with ForkJoinPool.

ForkJoinPool, like ThreadPoolExecutor, implements the Executor and ExecutorService interfaces. It uses an infinite queue to hold tasks that need to be executed, and the number of threads is passed in through the constructor. If the desired number of threads is not passed into the constructor, the number of cpus currently available on the computer is set to the number of threads as the default.

ForkJoinPool is mainly used to solve problems using a divide-and-conquer Algorithm. Typical applications are quicksort algorithms. The point here is that ForkJoinPool uses relatively few threads to handle a large number of tasks. For example, to sort 10 million pieces of data, the task would be split into two 5 million sort tasks and a merge task for those two 5 million pieces of data. Similarly, the same segmentation will be done for 5 million data, and a threshold value will be set at the end to stipulate when the data size reaches the threshold, such segmentation will be stopped. For example, when the number of elements is less than 10, splitting is stopped and insertion sort is used to sort them. So at the end of the day, all the missions add up to about 2 million + missions. The point is that a task cannot be executed until all of its subtasks have been completed. So using divide-and-conquer is problematic when using ThreadPoolExecutor because threads in ThreadPoolExecutor cannot add another task to the task queue and wait for it to complete before continuing to execute. When ForkJoinPool is used, a thread can create a new task and suspend the current one. The thread can then select a subtask from the queue. For example, if we need to count the number of elements less than 0.5 in a double array, we can use ForkJoinPool to do the following:

public class ForkJoinTest { private double[] d; private class ForkJoinTask extends RecursiveTask { private int first; private int last; public ForkJoinTask(int first, int last) { this.first = first; this.last = last; } protected Integer compute() { int subCount; if (last - first < 10) { subCount = 0; for (int i = first; i <= last; I ++) {if (d[I] < 0.5){subCount++; } } }else { int mid = (first + last) /2; ForkJoinTask left = new ForkJoinTask(first, mid); left.fork(); ForkJoinTask right = new ForkJoinTask(mid + 1, last); right.fork(); subCount = left.join(); subCount += right.join(); } return subCount; } } public static void main(String[] args) { ForkJoinPool pool=new ForkJoinPool(); pool.submit(new ForkJoinTask(0, 9999999)); pool.awaitTermination(2,TimeUnit.SECONDS); System.out.println("Found " + n + " values"); }}Copy the code

The key above is the fork() and join() methods. In ForkJoinPool threads, an internal queue is used to execute tasks and subtasks in order.

Note: There is a performance difference between Using ThreadPoolExecutor and ForkJoinPool:

(1) First, ForkJoinPool can use a limited number of threads to complete a very large number of parent-child tasks, such as more than two million tasks with four threads. However, with ThreadPoolExecutor, this is impossible because threads in ThreadPoolExecutor do not have the ability to preferentially execute subtasks. If you need to complete 2 million parent-child tasks, you also need 2 million threads, which is obviously not feasible.

(2) A ForkJoinPool is capable of Stealing Work. Each thread in the pool maintains a queue to hold tasks that need to be executed. When all the tasks in the thread’s queue are finished, it picks up unexecuted tasks from other threads and helps it execute them. As a result, thread utilization is improved, which improves overall performance.

(3) There is another factor affecting ForkJoinPool performance, which is the threshold at which task segmentation stops. For example, in the previous quicksort, subtask creation was stopped when the number of remaining elements was less than 10.

Conclusion:

When dealing with recursive divide-and-conquer algorithms, consider ForkJoinPool; Carefully set the threshold for no longer dividing tasks. This threshold affects performance. Several features in Java 8 use a common thread pool in ForkJoinPool. In some cases, you need to adjust the default number of threads for that thread pool. Seven, deadlocks

The four necessary conditions for a deadlock are as follows. If the following four conditions are met, a deadlock will occur, that is, if any of the conditions are not met, no deadlock will occur.

(1) Four necessary conditions for deadlock

Mutex conditions: resources cannot be Shared, can only be used by the same process requests and maintain conditions: have already got the process of resource can apply for a new resource not deprived conditions: have already allocated resources can not be forced to strip from the corresponding process cycle wait conditions: several processes in the system of the loop, the loop in the process of each process are waiting for the adjacent occupied resources

To give A common deadlock example: process A contains resource A, process B contains resource B, A's next step requires resource B, and B's next requires resource A, so they wait for each other's resources to be released, thus creating A circular wait deadlock.Copy the code

(2) Methods to deal with deadlocks

Ignore the problem, the ostrich algorithm. When something goes wrong, just skip it and ignore it. Detect deadlocks and recover; Dynamic allocation of resources; Break one of the four deadlock conditions above. Thread related classes

(1) the ThreadLocal

ThreadLocal is not a thread, but a data store class that can store data in a specified thread. After the data is stored, only the specified thread can obtain the data stored in the specified thread. Other threads cannot obtain the data of the specified thread. That is, multiple threads get different things from the same ThreadLocal, and even if sometimes the results are the same (by accident, two threads have two copies of the same thing), they get different things. Use this utility class to simplify concurrent access in multithreaded programming, and very succinctly isolate competing resources for multithreaded programs. In the case of multi-threaded resource sharing, synchronization takes a time-for-space approach, while ThreadLocal takes a space-for-time approach. The former provides only one copy of a variable, which different threads queue to access, while the latter provides one copy of a variable for each thread, so it can be accessed simultaneously without affecting each other. The ThreadLocal class provides the following three public methods:Copy the code

ThreadLocal() creates a thread-local variable. T get() returns the value in the current thread copy of this thread-local variable, which is created and initialized if this is the first time the thread has called the method. Protected T initialValue() returns the current thread’s initialValue for this thread-local variable. The following through the system source code to analyze the reason for this result. Two important methods exist in ThreadLocal, the get() and set() methods, each reading a setting.

** * Returns the value of this variable for the current thread. If an entry * doesn’t yet exist for this variable on this thread, this method will * create an entry, populating the value with the result of * {@link #initialValue()}. * * @return the current value of the variable for the calling thread. / @SuppressWarnings(“unchecked”) public T get() { // Optimized for the fast path. Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values ! = null) { Object[] table = values.table; int index = hash & values.mask; If (this.reference == table[index]) {return (T) table[index + 1]; }} else {values = initializeValues(currentThread); } the return (T) values. GetAfterMiss (this); } /* * Sets the value of this variable for the current thread. If set to * {@code null}, the value will be set to null and the underlying entry will * still be present. * * @param value the new value of the */ public void set(T value) {thread currentThread = thread.currentThread (); Values Values = Values (currentThread); If (values == null) {values = initializeValues(currentThread); } values.put(this, value); } The get method returns the value of the current thread’s variable, or creates a new array if it doesn’t exist. Also, for “current thread” and “array”, the array is a different values.table for each thread. Values is a value object obtained by the current thread. Therefore, this array is unique to each thread and cannot be shared. This explains why multiple threads are returning different things from the same ThreadLocal.

Why do you do this in Java?Copy the code

ThreadLocal is rarely used in everyday development, but in some special scenarios, it is easy to implement some seemingly complex functions. In general, you can consider using ThreadLocal when some data is thread-scoped and different threads have different copies of the data. Examples are Handler and Looper. The Handler needs to fetch the Looper of the current thread. Obviously Looper is the scope of the thread and different threads have different Loopers. Without ThreadLocal, the system would have to provide a global hash table for the Handler to find the specified Looper, which would be more cumbersome and require a management class. Another use of ThreadLocal is object passing under complex logic, such as listener passing. Sometimes tasks in a thread are too complex, which can be represented by a deep function call stack and a variety of code entries. In this case, we need listeners to be able to run through the execution of the thread. This is where ThreadLocal can be used. ThreadLocal allows listeners to exist as global objects within the thread, where they can be retrieved by the GET method. If you don’t, you can use parameter passing, but it’s not particularly well designed, and it’s a bad design to pass listeners by parameter when the call stack is deep. The other approach is to use static variables, but this approach has some limitations and is not particularly extensible. For example, if you have 10 threads executing, you need to provide 10 listener objects.

Note: ThreadLocal, like all synchronization mechanisms, is designed to resolve multiple access conflicts for the same variable. Value common synchronization mechanism, through the object lock to achieve multi-thread access to the same variable, and the variable is shared by multiple threads, all need to use this synchronization mechanism to clearly separate when the variable is read and write, when the need to lock the object. In this case, instead of making multiple copies of the resource, the system uses security mechanisms to control access. ThreadLocal is just another way to solve the problem of multi-threaded concurrent access, by making multiple copies of a resource that needs concurrent access. Each thread has one copy of the resource, and each thread has its own copy of the resource.

Summary: If multiple threads need to share resources to achieve communication between threads, use synchronization mechanism; If you only need to isolate the relational resources between multiple threads, you can use ThreadLocal.

The last

I here organized a Java multithreading information document, Spring series of family barrel, Java systematic information :(including Java core knowledge, interview topics and 20 years of the latest Internet real questions, e-books, etc.) have the need of friends can pay attention to the public number [procedures yuan small wan] can be obtained.