• Threads and processes

    Process: A program that runs independently in memory. Each process has its own memory space. A process can create more than one thread. Is the basic unit of resources allocated by the operating system.

    Thread: Created by a process to perform tasks. A process can have at least one thread and multiple threads. Threads share data in the process. Each thread has its own program counter, virtual stack, and local method stack, so the system costs much less to produce a single thread or switch work among multiple threads than the process, so the thread is also called lightweight process. The basic unit of scheduling and execution of a processor’s tasks

  • There are three ways to create threads

    • Override the Thread’s run method

      Thrad thread = new Thread(){
              @Override
              public void run(){
                 System.out.println(1)
              }
      };
      thread.start();
  • Implement runnable

    Thread thread = new Thread(new Runnable(){
             @Override
             public void run(){
                     System.out.println(2)
             }
    });
    thread.start();
  • To implement the callback

    Callable<Integer> callable = new Callable<Integer>(){ @Override public Integer call() throw Exception{ return 3; }}; FutureTask<Integer> task = new FutureTask<>(callable); new Thread(task).start(); while(task.isDone()){ try{ System.out.println(task.get()); }catch(InterruptedException e){ e.printStackTrace(); }catch(ExecutionException e){ e.printStackTrace(); }}
  • State of thread
  • NEW: The creation has not been executed, that is, the start() method has not been called
  • Runnable: Execute state, entered by calling the start() method
  • Blocked: The state in which the lock is not available after the start() method is called
  • Waiting: WAITING state, call start() method to get the lock and find no resources when executing, release the lock to another thread, enter this state, wait until there are resources, continue to execute.
  • Timed_Waiting: Timeout waits. After the start() method is called to get the lock and execute, it finds that there is no resource, releases the lock to other threads and enters the timeout waiting state. However, when the set waiting time is exceeded, the execution will continue even if the resource is not available.
  • TERMINATED: Thread execution end state
  • The difference between ‘wait’ and ‘sleep’

    • wait

      1. Pause the current thread and release the lock
      2. Belonging to an Object
      3. Wait can only be used in synchronized blocks
      4. You can wake up at any time
    • Sleep

      1. Pause the current thread without releasing the lock
      2. Belonging to Thread
      3. It can be used in any situation
      4. Only sleep to the set time will be reawakened
  • Why use multithreading

    Improve the efficiency of the program. Thread creation is a very expensive, the use of thread pool can avoid the thread creation and destruction caused by performance overhead, avoid a large number of threads due to preempting system resources blocked, can provide simple management of threads, timed execution, interval execution, etc.

  • What is a thread pool

    The basic idea of thread pool is a kind of object pool, which is to open up a space when the program is started, and then store threads (not dead), and the execution of the threads inside the thread pool to handle. When thread processing is needed, an object is obtained from the pool to execute the task. After the task is completed, the thread pool is put back into the thread pool. In this way, the overhead of new threads is avoided and the threads already created can be served to achieve the purpose of saving resources.

  • The main component of the thread pool

    • Thread pool manager -ThreadPool

      Use to create threads and manage threads, including create thread pool, destroy thread pool, add tasks

    • Worker thread -WorkThread

      A thread in a thread pool that is used to execute a task and is in a wait state when no task is available

    • Task interface -Task

      The interface that each task must implement for threads to schedule task execution, specifying the entry point of the task, the end work of the task, and the execution state of the task

    • Task queue – TaskQueue

      Tasks that have not been scheduled are placed in this place, waiting for the worker thread to schedule execution, which acts as a buffer.

  • Method to create a thread pool

    • newCachedThreadPool

      A cacheable thread pool is created. If the thread pool is longer than required for processing, the idle thread is retracted. If not, a new thread is created.

      public static void main(String[] args) { ExecutorService threadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 1000; i++) { final int index = i; try { Thread.sleep(index * 1000); } catch (Exception e) { e.printStackTrace(); } threadPool.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+":"+index); }}); }}
  • newFixedThreadPool

    A fixed-length thread pool is created, with the number of threads specified at creation time. Extra threads will wait in the queue.

    public static void main(String[] args) { ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); for (int i = 0; i < 10; i++) { final int index = i; fixedThreadPool.execute(new Runnable() { @Override public void run() { try { System.out.println(Thread.currentThread().getName() + ":" + index); // three threads concurrently Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }}}); }}
  • newScheduledThreadPool

    Create a fixed length thread pool, support periodic execution of the task

    public static void main(String[] args) { ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); / / said after 1 second delay. To do this every 3 seconds scheduledThreadPool scheduleAtFixedRate (new Runnable () {@ Override public void the run () { System.out.println(Thread.currentThread().getName() + ": delay 1 seconds, and excute every 3 seconds"); } }, 1, 3, TimeUnit.SECONDS); }
  • newSingleThreadExecutor

    Creating a thread pool with only one thread will only execute tasks with a single worker thread, ensuring that all tasks are processed in the order (FIFO,LIFO, priority) in the queue.

    public static void main(String[] args) { ExecutorService singleThreadPool = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int index = i; singleThreadPool.execute(new Runnable() { @Override public void run() { try { System.out.println(Thread.currentThread().getName() + ":" + index); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }}}); }}
  • The key class to create the thread pool is ThreadPoolExecutor

    ThreadPoolExecutor is one of the key classes for creating a thread pool. The UML class diagram of ThreadPoolExecutor in the JDK is shown below:

We can use ThreadPoolExecutor to create a thread pool

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

From the constructor we can see several parameters needed to create a thread pool

  • corePoolSize

    The base size of the thread pool. When a task is submitted to the thread pool, a thread is created to execute the task, even if other idle base threads are able to execute a new task, until the number of tasks to execute is greater than the base size of the thread pool. If the thread pool’s PreStartAllCoreThreads method is called, the pool creates and starts all the base threads ahead of time.

  • maximumPoolSize

    Thread pool maximum size, the maximum number of threads allowed to be created by the thread pool. If the queue is full and the number of threads created is less than the maximum number, the thread pool creates another thread to perform the task. It is important to note that this parameter has no effect if an unbounded task queue is used.

  • keepAliveTime

    The time that a thread pool remains alive after the worker thread is idle. So if there are many tasks and each task takes a short time to execute, you can increase this time to improve the thread utilization.

  • TimeUnit

    Thread activity holds time in units. Optional units include DAYS, HOURS, MINUTES, MILLISECONDS, and MILLISECONDS. Microseconds (thousandths of a millisecond) and NANOSECONDS (thousandths of a millisecond).

  • workQueue

    Task queue, a blocking queue that holds tasks waiting to be executed.

    BlockingQueue workQueue = null; workQueue = new ArrayBlockingQueue<>(5); // Array-based FIFO queue, bounded WorkQueue = new LinkedBlockingQueue<>(); SynchronousQueue<>(); SynchronousQueue<>(); // Unbuffered wait queue, unbounded
According to ` Java. Util. Concurrent. ThreadPoolExecutor# execute ` source can be seen that the thread pool processing ` ` ` Java # for the number of threads int c = CTL. The get (); If (WorkWorker (c) < CorePoolSize) {if (addWorker(command, true)) return; c = ctl.get(); } #2. If the number of threads has reached corePools, move the task to the queue and wait for if (IsRunning (C) && WorkQueue.offer (Command)) {int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); } #3. Queue full, new thread (non-core thread) to execute task addWorker(command, false)) #4. With the queue full and the total number of threads reaching MaximumPoolSize, Reject (command) will be thrown by RejectedExecutionHandler. ` ` `
  • threadFactory

    The Thread Factory, which is used to set up the factory where threads are created, can be used to give each thread a more meaningful name, Debug and locate problems.

  • RejectedExecutionHandler

    Reject policy. When both the queue and the thread pool are full, indicating that the thread pool is saturated, a policy must be adopted to handle the submitted new tasks. This policy, by default, is AbortPolicy, which throws an exception when a new task cannot be processed.

    Here are the four policies that JDK1.5 provides:

    AbortPolicy: discard task and throw RejectedExecutionException anomalies.

    DiscardPolicy: Discards tasks, but does not throw exceptions.

    Discard the top task in the queue and submit the new task.

    CallerRunPolicy: Process the task by the calling thread (the thread that submitted the task, the main thread).

  • The state of the thread pool

  1. The thread pool is initialized in RUNNING and can receive new tasks as well as process tasks that have been added.
  2. When the thread pool is SHUTDOWN, no new tasks are received, but added tasks can be processed. When the thread pool’s shutdown() interface is called, the thread pool is shutdown by run->.
  3. When the thread pool is in the STOP state, it does not receive new tasks, does not process added tasks, and interrupts tasks that are being processed. When the thread pool shutdownNow() interface is called, the thread pool is stopped by (RUNNING or SHUTDOWN) ->.
  4. When all the tasks have been terminated and the “number of tasks” recorded by the CTL is 0, the thread pool becomes TIDYING. Hook function terminated() when the thread pool is in the Tidying state. Terminated () is empty in the ThreadPoolExecutor class, and the user will process it if the thread pool becomes TIDYING. This can be done by overloading the terminated() function.
  5. When the thread pool is in the SHUTDOWN state, the blocking queue is empty and the tasks being executed in the thread pool are also empty, then the SHUTDOWN -> TIDYING occurs.
  6. When the thread pool is in the STOP state and the task executing in the thread pool is empty, it is stopped -> TIDYING. The thread pool terminates completely and becomes TERMINATED. Tidying -> terminated after execution () with the thread pool in the TIDYING state.
  • Why does the Ali Java Development Manual recommend creating a thread pool manually instead of using Executors

    The NewFixedThreadPool and NewSingleThreadExecutor are mainly due to the fact that the stacked request processing queues can be very memory intensive, even OOM.

    #newSingleThreadExecutor
    public static ExecutorService newSingleThreadExecutor() {
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>()));
        }
     
    #newFixedThreadPool
    public static ExecutorService newFixedThreadPool(int nThreads) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>());
        }

As can be seen from the source code, these two thread pools use LinkedBlockingQueue as the task queue. This is an unbounded queue. Even if you limit the number of threads, the tasks will still be placed in the queue for stashing, eventually resulting in OOM.

NewCachedThreadPool and NewScheduledThreadPool mainly because the maximum number of threads is Integer.MAX_VALUE, it is possible to create a very large number of threads, even OOM.

#newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  
#ScheduledThreadPoolExecutor
 public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

MaximumPoolSize is Integer.MAX_VALUE, which can result in too many beds and too many threads if there are too many tasks.