java thread pool understanding

If you still have doubts about what is a thread and what is a process, please Google it first, as these two concepts are beyond the scope of this article.

There is only one purpose to use multithreading, and that is to make better use of cpu resources, because all multithreaded code can be implemented with a single thread. This is actually only half right, because the program code that reflects "multi-role", at least each character must be given a thread, otherwise even the actual scene can't be simulated, and of course it can't be implemented with a single thread: For example, the most common "producer, consumer model".

Many people are not clear about some of these concepts, such as synchronization, concurrency, etc. Let's build a data dictionary first to avoid misunderstandings.

  • Multithreading: refers to the fact that more than one thread is generated when the program (a process) is running
  • Parallelism and Concurrency:
    • Parallelism: Multiple CPU instances or multiple machines execute a piece of processing logic at the same time, which is true at the same time.
    • Concurrency: Through the cpu scheduling algorithm, users can appear to execute at the same time, but in fact, it is not real at the same time from the cpu operation level. Concurrency often has common resources in the scene, so this common resource often creates a bottleneck. We will use TPS or QPS to reflect the processing capacity of the system.

                            

Concurrency and Parallelism

  • Thread Safety: Often used to delineate a piece of code. It means that in the case of concurrency, the code is used by multiple threads, and the scheduling order of threads does not affect any results. At this time, using multi-threading, we only need to pay attention to the memory of the system and whether the cpu is enough. In turn, thread unsafe means that the scheduling order of threads will affect the final result, such as the transfer code without transaction:
    void transferMoney(User from, User to, float amount){
      to.setMoney(to.getBalance() + amount);
      from.setMoney(from.getBalance() - amount);
    }
  • Synchronization: Synchronization in Java refers to ensuring that multi-threaded access to shared resources becomes thread-safe through artificial control and scheduling to ensure accurate results. Simply add @synchronizedkeywords as in the code above. It is an excellent program to improve performance while ensuring accurate results. Thread safety takes precedence over performance.

Alright, let's get started. I'm going to break it down into sections to summarize what's involved with multithreading:

  1. Studied horses: the state of the thread
  2. The inner strength method: the method (mechanism) that each object has
  3. Taizu Changquan: Basic Thread Class
  4. Jiuyin Mantra: Advanced Multithreaded Control Class

Studied horses: the state of the thread

First two pictures:

thread state

thread state transition


The various states are clear at a glance. It is worth mentioning the "blocked" state: the
thread may encounter a blocked (Blocked) situation during the Running process.

  1. When the join() and sleep() methods are called, the sleep() time ends or is interrupted, the join() is interrupted, and the IO completion will return to the Runnable state, waiting for the JVM to schedule.
  2. Call wait() to keep the thread in the waiting pool (wait blocked pool) until notify()/notifyAll(), the thread is woken up and placed in the lock pool (lock blocked pool), release the synchronization lock to return the thread to a runnable state (Runnable)
  3. Add a synchronization lock (Synchronized) to the thread in the Running state to make it enter (lock blocked pool), and the synchronization lock is released to enter the runnable state (Runnable).

In addition, the thread in the runnable state is the thread that is being scheduled, and the scheduling order at this time is not certain. The yield method in the Thread class can make a thread in the running state turn into a runnable.

The inner strength method: the method (mechanism) that each object has

synchronized, wait, notify are synchronization tools that any object has. Let's get to know them first

monitor
They are artificial thread scheduling tools applied to synchronization problems. Speaking of its essence, we must first clarify the concept of monitor. Every object in Java has a monitor to monitor the reentrancy of concurrent code. The monitor does not play a role in non-multithreaded coding, and vice versa if in the synchronized scope, the monitor works.

wait/notify must exist in a synchronized block. Also, these three keywords refer to the same monitor (an object's monitor). This means that after the wait, other threads can enter the synchronized block execution.

When a code does not hold the right to use the monitor (the state in Figure 5, that is, leaving the synchronization block) to wait or notify, a java.lang.IllegalMonitorStateException will be thrown. It also includes calling wait/notify of another object in the synchronized block, because different objects have different monitors, and this exception will also be thrown.

Let's talk about usage:

  • Use synchronized alone:
    • Code block: As follows, in a multi-threaded environment, the method in the synchronized block obtains the monitor of the lock instance. If the instances are the same, only one thread can execute the block content

      Press Ctrl+C to copy the code

      Press Ctrl+C to copy the code

    • Directly used in the method: It is equivalent to the effect of locking with lock in the above code, and what is actually obtained is the monitor of the Thread1 class. Further, if the modification is a static method, all instances of the class are locked.
      public class Thread1 implements Runnable {
         public synchronized void run() {  
              ..do something
         }
      }
  • Synchronized, wait, notify combination: typical scenario producer-consumer problem

    Press Ctrl+C to copy the code

    Press Ctrl+C to copy the code

    volatile

    Multi-threaded memory model: main memory (main memory), working memory (thread stack), when processing data, the thread will load the value from the main memory to the local stack, and then save it back after completing the operation (the role of the volatile keyword: Each operation on the variable fires a load and save).

 

volatile

If a variable used by multiple threads is not volatile or final, it is likely to produce unpredictable results (another thread modifies the value, but then a thread sees the value before the modification). In fact, in principle, there is only one copy of the same property of the same instance itself. But multithreading will cache the value. In essence, volatile is not to cache, but to take the value directly. Adding volatile in the case of thread safety will sacrifice performance.

Taizu Changquan: Basic Thread Class

The basic thread class refers to the Thread class, the Runnable interface, and the Callable interface. The
Thread class implements the Runnable interface and starts a thread method:

 MyThread my = new MyThread();
  my.start();

Thread class related methods:

//The current thread can transfer cpu control and let other ready state threads run (switch)
public static Thread.yield()
//pause for a while
public static Thread.sleep()  
//Call other.join() in a thread, it will wait for other to finish executing before continuing the thread.    
public join()
//The last two functions can be interrupted
public interrupte()

Regarding interrupts : it does not interrupt a running thread like the stop method does. From time to time, the thread checks the interrupt flag bit to determine whether the thread should be interrupted (whether the interrupt flag value is true). The terminal only affects the wait state, sleep state and join state. Interrupted threads throw InterruptedException.
Thread.interrupted() checks whether the current thread is interrupted, and returns boolean
synchronized cannot be interrupted in the process of acquiring the lock.

Interruption is a state! The interrupt() method just sets this state to true. Therefore, a normally running program will not terminate if it does not detect the state, and blocking methods such as wait will check and throw an exception. If you add while(!Thread.interrupted()) in a normal running program, you can also leave the code body after interruption

Best practice for Thread class :
When writing, it is best to set the thread name Thread.name, and set the thread group ThreadGroup, the purpose is to facilitate management. When a problem occurs, print the thread stack (jstack -pid) to see at a glance which thread has the problem and what the thread is doing.

How to get exception in thread

Can't use try, catch to get exceptions in threads

Runnable

Similar to Thread

Callable

future mode: a kind of concurrent mode, which can have two forms, namely non-blocking and blocking, which are isDone and get respectively. The Future object is used to store the return value and status of the thread

ExecutorService e = Executors.newFixedThreadPool(3);
 //The submit method has a multi-parameter version, and supports callable and runnable interface types.
Future future = e.submit(new myCallable());
future.isDone() //return true,false without blocking
future.get() // return return value, block until the thread finishes running

Jiuyin Mantra: Advanced Multithreaded Control Class

All of the above belong to the inner strength method. Next are the tools commonly used in actual projects. Java 1.5 provides a very efficient and practical multi-threading package: java.util.concurrent , which provides a large number of advanced tools that can help developers write Efficient, easy to maintain, clearly structured Java multithreaded program.

1.ThreadLocal类

Useful for: Save thread-independent variables. For a thread class (inherited from Thread)
when using ThreadLocal to maintain variables, ThreadLocal provides an independent copy of the variable for each thread that uses the variable, so each thread can change its own copy independently without affecting other threads the corresponding copy. Commonly used for user login control, such as recording session information.

Implementation: Each Thread holds a variable of type TreadLocalMap (this class is a lightweight Map, the function is the same as the map, the difference is that the entry is placed in the bucket instead of the linked list of entries. The function is still a map.) The key itself is the key, and the target is the value.
The main methods are get() and set(T a). After set, a threadLocal -> a is maintained in the map, and a is returned during get. ThreadLocal is a special container.

2. Atomic class (AtomicInteger, AtomicBoolean...)

If you use an atomic wrapper class such as atomicInteger, or use your own guaranteed atomic operations, it is equivalent to synchronized

//The return value is boolean
AtomicInteger.compareAndSet(int expect,int update)

This method can be used to implement optimistic locking. Consider the following scenario initially mentioned in the article: a pays b 10 yuan, a deducts 10 yuan, and b needs to add 10 yuan. At this time, c gives b2 yuan, but the code for adding ten yuan to b is about:

if(b.value.compareAndSet(old, value)){
   return ;
}else{
   //try again
   // if that fails, rollback and log
}

AtomicReference
For AtomicReference, the object may appear and the property is lost, that is, oldObject == current, but oldObject.getPropertyA != current.getPropertyA.
This is where AtomicStampedReference comes in handy. This is also a very common idea, that is, adding a version number

3. Lock class 

lock: in the java.util.concurrent package. There are three implementations:

ReentrantLock
ReentrantReadWriteLock.ReadLock
ReentrantReadWriteLock.WriteLock

The main purpose is the same as synchronized, both of which are technologies for solving synchronization problems and dealing with resource disputes. The functionality is similar with some differences.

The difference is as follows:

The lock is more flexible, and the unlocking sequence of the shackles of multiple locks can be freely defined (synchronized should be added first and then unlocked)
Provides a variety of locking schemes, lock blocking, trylock non-blocking, lockInterruptily interruptible, and trylock with a timeout version.
Essentially the same as monitor locks (ie synchronized)
The greater the ability, the greater the responsibility, the locking and unlocking must be controlled, otherwise it will lead to disaster.
Combined with the Condition class.
The performance is higher, as shown in the following figure:

Synchronized and Lock performance comparison

The meaning of ReentrantLock    
's reentrancy is that the thread holding the lock can continue to hold it, and the lock is only released after the number of times the peer is released.
The usage method is:

1. First new an instance

static ReentrantLock r=new ReentrantLock();
2. Lock      
r.lock()或r.lockInterruptibly();

Here too, the latter can be interrupted. When thread a is locked, thread b is blocked. If it is lockInterruptibly at this time, then after calling b.interrupt(), thread b exits the blocking, gives up the contention for resources, and enters the catch block. (if the latter is used, must throw interruptable exception or catch)    

3. Release the lock   

r.unlock()

must do! Why must it be done? It should be placed in finally. In order to prevent the exception from jumping out of the normal process, leading to disaster. Here is a small knowledge point, finally can be trusted: after testing, even if an OutofMemoryError occurs, the execution of the statements in the finally block can be guaranteed.

ReentrantReadWriteLock

Reentrant read-write lock (an implementation of read-write lock)

 ReentrantReadWriteLock lock = new ReentrantReadWriteLock()
  ReadLock r = lock.readLock();
  WriteLock w = lock.writeLock();

Both have lock, unlock methods. Write and write, write and read are mutually exclusive; read and read are not mutually exclusive. Efficient thread-safe code that can implement concurrent reads

4. Container class

Here are two of the more commonly used ones:

BlockingQueue
ConcurrentHashMap

BlockingQueue
blocking queue. This class is an important class under the java.util.concurrent package. Through the study of Queue, we can know that this queue is a one-way queue, which can add elements at the head of the queue and delete or remove elements at the end of the queue. Similar to a pipeline, it is especially suitable for some application scenarios of FIFO strategy. The common queue interface is mainly implemented with PriorityQueue (priority queue), you can study if you are interested

BlockingQueue adds the function of multi-threaded cooperation to the queue:

BlockingQueue


In addition to the traditional queue function (two columns on the left of the table), there are also blocking interfaces put and take, and blocking interfaces offer and poll with timeout function. put will block when the queue is full and will be awakened when there is space; take will block when the queue is empty and will not be awakened until there is something to take. It is especially useful for the producer-consumer model, which is an artifact.

Common blocking queues are:

ArrayListBlockingQueue
LinkedListBlockingQueue
DelayQueue
SynchronousQueue

ConcurrentHashMap
efficient thread-safe hash map. Please compare hashTable, concurrentHashMap, HashMap

5. Management class

The concept of management class is relatively broad, and it is used to manage threads. It is not multi-threaded, but provides some mechanisms to use the above tools to do some encapsulation.
The management class that is worth mentioning: ThreadPoolExecutor and the system-level management class ThreadMXBean under the JMX framework. If
ThreadPoolExecutor
does not understand this class, you should understand the aforementioned ExecutorService, and it is very convenient to open your own thread pool:

ExecutorService e = Executors.newCachedThreadPool();
    ExecutorService e = Executors.newSingleThreadExecutor();
    ExecutorService e = Executors.newFixedThreadPool(3);
    // The first is a variable size thread pool, which allocates threads according to the number of tasks.
    // The second is a single thread pool, equivalent to FixedThreadPool(1)
    // The third is a fixed size thread pool.
    // then run
    e.execute(new MyRunnableImpl());

This class is internally implemented by ThreadPoolExecutor. Mastering this class helps to understand the management of thread pools. In essence, they are all implementation versions of the ThreadPoolExecutor class. See javadoc:

ThreadPoolExecutor parameter explanation
Translate:

corePoolSize: The initial value and minimum value of the threads in the pool, even if it is idle, the number of threads will be maintained.
maximumPoolSize: The maximum value of the thread, the growth of the thread will never exceed this value.
keepAliveTime: When the number of threads in the pool is higher than corePoolSize, how long will the excess idle threads be recycled. Wait state before recycling
unit:
Time unit, you can use an instance of TimeUnit, such as TimeUnit.MILLISECONDS 
workQueue: The waiting place for the task to be entered (Runnable), this parameter mainly affects the scheduling strategy, such as fairness or not, whether there is starving (starving)
threadFactory: The thread factory class has a default implementation. If you have custom needs, you need to implement the ThreadFactory interface yourself and pass it in as a parameter.

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325587273&siteId=291194637