Java thread pool security issues

1. My classmates say that I am a blogger but not a master, and that I am not pure, but I am neither a blogger nor a miscellaneous, so there is still a long way to go. I don’t know when this road will end, but I think Painful and happy to move on

Reprinted: https://www.cnblogs.com/wxd0108/p/5479442.html

quote

There is only one purpose of using multithreading, which is to make better use of cpu resources, because all multithreaded codes can be implemented with single threads. In fact, this is only half true, because the program code that reflects “multi-role”, at least each character must be given a thread, otherwise the actual scene cannot be simulated, and of course it cannot be realized with a single thread: For example, the most common “producer, consumer model”.

Many people are not clear about some of the concepts, such as synchronization, concurrency, etc. Let us first create a data dictionary to avoid misunderstandings.

  • Multithreading: refers to the fact that more than one thread is generated when the program (a process) runs
  • Parallel and concurrent:
    • Parallelism: Multiple CPU instances or multiple machines execute a piece of processing logic at the same time, which is truly simultaneous.
    • Concurrency: Through the cpu scheduling algorithm, the users seem to execute at the same time, but in fact, it is not really simultaneous from the cpu operation level. Concurrency often has common resources in the scene, so bottlenecks often occur for this common resource, and we will use TPS or QPS to reflect the processing capacity of this system.

Concurrency and Parallelism

  • Thread Safety: Often used to describe a piece of code. It means that in the case of concurrency, the code is used by multiple threads, and the scheduling order of the threads does not affect any results. At this time, when using multithreading, we only need to pay attention to the memory of the system and whether the cpu is enough. Conversely, thread insecurity means that the scheduling order of threads will affect the final result, such as 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 the accuracy of results. As in the above code, simply add the @synchronized keyword. 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 several parts to summarize what multithreading involves:

  1. Getting your foot in the door: the state of threads
  2. Inner strength method: each object has a method (mechanism)
  3. Taizu Changquan: basic thread class
  4. Nine Yin Scriptures: Advanced Multi-threaded Control

Get on the horse: the state of the thread

Here are two pictures:

thread state

thread state transition

The various states are clear at a glance, and it is worth mentioning the “blocked” state:
The thread may encounter a blocked (Blocked) situation during the running process

  1. Call the join() and sleep() methods, the sleep() time ends or is interrupted, the join() is interrupted, and the IO is completed, it will return to the Runnable state, waiting for the JVM to schedule.
  2. Call wait() to make the thread in the waiting pool (wait blocked pool) until notify()/notifyAll(), the thread is awakened and put into the lock pool (lock blocked pool), and the synchronization lock is released to make the thread return to the runnable state (Runnable)
  3. Add a synchronization lock (Synchronized) to the thread in the Running state to make it enter the (lock blocked pool), and the synchronization lock is released to enter the runnable state (Runnable).

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

Inner strength method: each object has a method (mechanism)

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-multi-threaded coding, but if it is in the synchronized range, the monitor plays a role.

wait/notify must exist in a synchronized block. Moreover, these three keywords are aimed at the same monitor (a monitor of an object). This means that after wait, other threads can enter the synchronized block execution.

When a code does not hold the right to use the monitor (as shown in Figure 5, that is, out of 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 the monitors of different objects are different, and this exception will also be thrown.

Talk about usage again:

  • 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 content of the block

      copycode

      public class Thread1 implements Runnable {
         Object lock;
         public void run() {
             synchronized(lock){
               ..do something
             }
         }
      }

      copycode

    • Directly used in the method: It is equivalent to the effect of using lock to lock in the above code, and what is actually obtained is the monitor of the Thread1 class. Furthermore, if the modified method is a static method, all instances of the class are locked.
      public class Thread1 implements Runnable {
         public synchronized void run() {
              ..do something
         }
      }
  • The combination of synchronized, wait, and notify: producer-consumer problems in typical scenarios

    copycode

    /**
       * The products produced by the producer are handed over to the clerk
       */
      public synchronized void produce()
      {
          if(this. product >= MAX_PRODUCT)
          {
              try
              {
                  wait();
                  System.out.println("The product is full, please try again later");
              }
              catch(InterruptedException e)
              {
                  e.printStackTrace();
              }
              return;
          }
    
          this.product++;
          System.out.println("The producer produces the first " + this.product + "product.");
          notifyAll(); //Notify consumers in the waiting area that they can take out the product
      }
    
      /**
       * The consumer picks up the product from the clerk
       */
      public synchronized void consume()
      {
          if(this. product <= MIN_PRODUCT)
          {
              try
              {
                  wait();
                  System.out.println("out of stock, pick up later");
              }
              catch (InterruptedException e)
              {
                  e.printStackTrace();
              }
              return;
          }
    
          System.out.println("The consumer has taken the " + this.product + " product.");
          this. product--;
          notifyAll(); //Notify the waiting producers that they can produce products
      }

    copycode

    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 save it back after completing the operation (the role of the volatile keyword: Each operation on the variable triggers a load and save).

volatile

If the variable used for multi-threading is not volatile or final modified, it is very likely to produce unpredictable results (another thread modifies this value, but then a thread sees the value before the modification). In fact, the truth is that the same attribute of the same instance itself has only one copy. But multi-threading will cache the value. In essence, volatile means not to cache, but to take the value directly. Adding volatile to 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, the method to start a thread:

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

Thread class related methods:

copycode

//The current thread can transfer the cpu control right to let other ready state threads run (switch)
public static Thread. yield()
// Pause for a while
public static Thread. sleep()
//Calling other.join() in a thread will wait for the execution of the other before continuing the thread. the
public join()
//The last two functions can be interrupted
public interrupt()

copycode

About interruption: It does not interrupt a running thread like the stop method. The thread will detect the interrupt flag bit from time to time to determine whether the thread should be interrupted (whether the interrupt flag value is true). The terminal will only affect 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 during the lock acquisition process.

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 check the status, while blocking methods such as wait will check and throw an exception. If you add while(!Thread.interrupted()) to a normally running program, you can also leave the code body after the interruption

Thread class best practices:
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 does.

How to get the exception in the thread

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

Runnable

Similar to Thread

Callable

Future mode: A type 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 state of the thread

ExecutorService e = Executors. newFixedThreadPool(3);
 //The submit method has multiple parameter versions, and supports callable and runnable interface types.
Future future = e. submit(new myCallable());
future.isDone() //return true, false non-blocking
future.get() // return return value, block until the thread ends

Nine Yin Scriptures: Advanced Multi-threaded Control

The above are all internal skills, and the next is the tools commonly used in actual projects. Java1.5 provides a very efficient and practical multi-threading package: java.util.concurrent, which provides a large number of advanced A tool that can help developers write efficient, easy-to-maintain, and clearly structured Java multi-threaded programs.

1.ThreadLocal class

Uses: Save the independent variables of the thread. 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 the corresponding copies of other threads. Often 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 with the same function as map, the difference is that the linked list of entry is placed in the bucket instead of entry. The function is still a map.) It is the key itself, 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 when getting. ThreadLocal is a special container.

2. Atomic classes (AtomicInteger, AtomicBoolean…)

If you use an atomic wrapper class such as atomicInteger, or use an operation that guarantees atomicity yourself, 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 of 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, maybe the object will appear, and the property will be lost, that is, oldObject == current, but oldObject.getPropertyA != current.getPropertyA.
At this time, AtomicStampedReference comes in handy. This is also a very common idea, that is, add the 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 produced to solve synchronization problems and handle resource disputes. Functionally similar but with some differences.

The difference is as follows:

copycode

lock is more flexible, you can freely define the unlocking sequence of multiple locks (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.
In essence, it is the same as monitor lock (that is, synchronized)
The greater the ability, the greater the responsibility. Locking and unlocking must be well controlled, otherwise it will lead to disaster.
Combination with Condition class.
Higher performance, compared with the following picture:

copycode

Synchronized and Lock performance comparison

ReentrantLock
The meaning of reentrancy is that the thread holding the lock can continue to hold it, and the lock will not be released until the number of peers is released.
The method of use is:

1. First new an instance

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

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

3. Release the lock

r. unlock()

Must do! Why must it be done? Put it in finally. To prevent exceptions from jumping out of the normal process and causing disasters. Here is a small knowledge point to add, finally can be trusted: after testing, even if an OutofMemoryError occurs, the execution of the statement 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 and unlock methods. Write and write, write and read are mutually exclusive; read and read are not mutually exclusive. Efficient thread-safe code that enables 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, and elements can be added at the head of the queue and deleted or taken out at the end of the queue. Similar to a pipeline, it is especially suitable for some application scenarios of the first-in first-out policy. The main implementation of the common queue interface is PriorityQueue (priority queue), which can be studied if you are interested

BlockingQueue adds the function of multi-thread cooperation on the basis of queue:

BlockingQueue

In addition to the traditional queue function (the two columns on the left of the table), it also provides 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 woken up when there is space; take will block when the queue is empty, and will not be woken up until there is something to take. It is especially useful for producer-consumer models, and it can be called an artifact.

Common blocking queues are:

ArrayListBlockingQueue
LinkedListBlockingQueue
DelayQueue
SynchronousQueue

ConcurrentHashMap
Efficient thread-safe hash maps. Please compare hashTable, concurrentHashMap, HashMap

5. Management

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

copycode

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-threaded pool, equivalent to FixedThreadPool(1)
    // The third is a fixed size thread pool.
    // then run
    e. execute(new MyRunnableImpl());

copycode

This class is implemented internally through ThreadPoolExecutor. Mastering this class is helpful for understanding the management of thread pools. In essence, they are all various implementation versions of the ThreadPoolExecutor class. See the javadoc:

ThreadPoolExecutor parameter explanation

translate:

copycode

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

copycode

1. Threads are non-daemon threads by default. Non-daemon threads are also called user threads. A thread can set itself as a daemon thread by calling the void setDaemon(boolean on) method. thread.setDaemon(true), when the user thread is executed, there are other statements that need to be executed in the run method in the daemon thread, and the daemon thread will be terminated immediately.

public class ShouHuXianCheng {
public static void main(String[] args) {
// TODO Auto-generated method stub
Dameon a = new Dameon();
a.A.start();
a.B.setDaemon(true);
a.B.start();
}
}
class Dameon implements Runnable{
Thread A,B;
Dameon(){
A=new Thread(this);
B=new Thread(this);
}
public void run(){
if(Thread. currentThread()==A){
for(int i=0;i<8;i ++ ){
System.out.println(“i + ” + i);
try{
Thread. sleep(1000);
}catch(InterruptedException ex){
}
}
}
else if(Thread. currentThread()==B){
while(true){
System.out.println(“Thread B is a daemon thread”);
try{
Thread. sleep(1000);
}catch(InterruptedException ex){
}
}
}
}
}