Home  >  Article  >  Java  >  Analysis of four Java thread pool usages

Analysis of four Java thread pool usages

高洛峰
高洛峰Original
2017-01-23 16:26:041414browse

This article analyzes four Java thread pool usages for your reference. The specific content is as follows

1. Disadvantages of new Thread

To perform an asynchronous task, do you still just use new Thread as follows? ?

new Thread(new Runnable() {
 
  @Override
  public void run() {
    // TODO Auto-generated method stub
    }
  }
).start();

Then you have too many outs. The disadvantages of new Thread are as follows:

a. Every time new Thread creates an object, the performance is poor.
b. Threads lack unified management, and there may be unlimited new threads, competing with each other, and may occupy too many system resources, causing crashes or OOM.
c. Lack of more functions, such as scheduled execution, periodic execution, and thread interruption.

Compared with new Thread, the benefits of the four thread pools provided by Java are:

a. Reuse existing threads, reduce the cost of object creation and destruction, and have good performance.
b. It can effectively control the maximum number of concurrent threads, improve the usage of system resources, and avoid excessive resource competition and congestion.
c. Provide functions such as scheduled execution, periodic execution, single thread, and concurrency control.

2. Java thread pool

Java provides four thread pools through Executors, namely:

newCachedThreadPool creates a cacheable thread pool. If the thread pool length exceeds processing needs , which can flexibly recycle idle threads. If there is no recyclable thread, a new thread will be created.
newFixedThreadPool creates a fixed-length thread pool that can control the maximum number of concurrent threads. Exceeding threads will wait in the queue.
newScheduledThreadPool creates a fixed-length thread pool that supports scheduled and periodic task execution.
newSingleThreadExecutor creates a single-threaded thread pool, which will only use a unique worker thread to execute tasks, ensuring that all tasks are executed in the specified order (FIFO, LIFO, priority).
(1)newCachedThreadPool:

Create a cacheable thread pool. If the length of the thread pool exceeds processing needs, idle threads can be flexibly recycled. If there is no way to recycle, create a new thread. The sample code is as follows:

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
  for (int i = 0; i < 10; i++) {
    final int index = i;
  try {
    Thread.sleep(index * 1000);
  }
    catch (InterruptedException e) {
      e.printStackTrace();
  }
 
cachedThreadPool.execute(new Runnable() {
 
@Override
public void run() {
  System.out.println(index);
}
});
}


The thread pool is infinite. When the second task is executed, the first task has been completed and will be reused. The thread of the first task instead of creating a new thread each time.

(2)newFixedThreadPool:

Create a fixed-length thread pool that can control the maximum number of concurrent threads. Exceeding threads will wait in the queue. The sample code is as follows:

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(index);
  Thread.sleep(2000);
} catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
  }
}
});
}

Because the thread pool size is 3, each task sleeps for 2 seconds after outputting the index, so 3 numbers are printed every two seconds.

The size of the fixed-length thread pool is best set according to system resources. Such as Runtime.getRuntime().availableProcessors(). Please refer to PreloadDataCache.

(3)newScheduledThreadPool:

Create a fixed-length thread pool to support scheduled and periodic task execution. The sample code for delayed execution is as follows:

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
 scheduledThreadPool.schedule(new Runnable() {
 
@Override
public void run() {
  System.out.println("delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);

means delayed execution for 3 seconds.

The sample code for regular execution is as follows:

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
 
@Override
public void run() {
  System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);

means execution every 3 seconds after a delay of 1 second.

ScheduledExecutorService is safer and more powerful than Timer

(4)newSingleThreadExecutor:

Create a single-threaded thread pool, which will only use the only worker thread to Execute tasks and ensure that all tasks are executed in the specified order (FIFO, LIFO, priority). The sample code is as follows:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
 
@Override
public void run() {
  try {
    System.out.println(index);
  Thread.sleep(2000);
} catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
    }
}
  });
}

The results are output in sequence, which is equivalent to executing each task in sequence.

Most current GUI programs are single-threaded. Single threads in Android can be used for database operations, file operations, batch installation of applications, batch deletion of applications, and other operations that are not suitable for concurrency but may cause IO blocking and affect the response of the UI thread.

The role of the thread pool:

The role of the thread pool is to limit the number of execution threads in the system.
According to the system environment, the number of threads can be set automatically or manually to achieve the best operation effect; less will waste system resources, and more will cause system congestion and inefficiency. Use the thread pool to control the number of threads, and other threads wait in line. After a task is executed, the frontmost task in the queue is taken and execution begins. If there is no waiting process in the queue, this resource of the thread pool is waiting. When a new task needs to be run, if there are waiting worker threads in the thread pool, it can start running; otherwise, it will enter the waiting queue.

Why use a thread pool:

1. Reduce the number of thread creation and destruction, each worker thread can be reused and can perform multiple tasks.

2. You can adjust the number of working threads in the thread pool according to the system's capacity to prevent the server from being exhausted due to excessive memory consumption (each thread requires about 1MB of memory, and the thread starts The more memory is consumed, the more memory will be consumed and eventually the system will crash).

The top-level interface of the thread pool in Java is Executor, but strictly speaking, Executor is not a thread pool, but just a tool for executing threads. The real thread pool interface is ExecutorService.

Some of the more important classes:

ExecutorService: The real thread pool interface.

ScheduledExecutorService: Similar to Timer/TimerTask, it can solve problems that require repeated execution of tasks.

ThreadPoolExecutor: The default implementation of ExecutorService.

ScheduledThreadPoolExecutor: Inherits the ScheduledExecutorService interface implementation of ThreadPoolExecutor, a class implementation of periodic task scheduling.

要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

1.newSingleThreadExecutor

创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

2.newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

3.newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,

那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

4.newScheduledThreadPool

创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

实例代码

一、固定大小的线程池,newFixedThreadPool:

package app.executors;
 
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
 
/**
 * Java线程:线程池
 *
 * @author xiho
 */
public class Test {
  public static void main(String[] args) {
    // 创建一个可重用固定线程数的线程池
    ExecutorService pool = Executors.newFixedThreadPool(2);
    // 创建线程
    Thread t1 = new MyThread();
    Thread t2 = new MyThread();
    Thread t3 = new MyThread();
    Thread t4 = new MyThread();
    Thread t5 = new MyThread();
    // 将线程放入池中进行执行
    pool.execute(t1);
    pool.execute(t2);
    pool.execute(t3);
    pool.execute(t4);
    pool.execute(t5);
    // 关闭线程池
    pool.shutdown();
  }
}
 
class MyThread extends Thread {
  @Override
  public void run() {
    System.out.println(Thread.currentThread().getName() + "正在执行。。。");
  }
}

输出结果:

pool-1-thread-1正在执行。。。
pool-1-thread-3正在执行。。。
pool-1-thread-4正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-5正在执行。。。

改变ExecutorService pool = Executors.newFixedThreadPool(5)中的参数:ExecutorService pool = Executors.newFixedThreadPool(2),输出结果是:

pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。

从以上结果可以看出,newFixedThreadPool的参数指定了可以运行的线程的最大数目,超过这个数目的线程加进去以后,不会运行。其次,加入线程池的线程属于托管状态,线程的运行不受加入顺序的影响。

二、单任务线程池,newSingleThreadExecutor:

仅仅是把上述代码中的ExecutorService pool = Executors.newFixedThreadPool(2)改为ExecutorService pool = Executors.newSingleThreadExecutor();
输出结果:

pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。

可以看出,每次调用execute方法,其实最后都是调用了thread-1的run方法。

三、可变尺寸的线程池,newCachedThreadPool:

与上面的类似,只是改动下pool的创建方式:ExecutorService pool = Executors.newCachedThreadPool();

输出结果:

pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-4正在执行。。。
pool-1-thread-3正在执行。。。
pool-1-thread-5正在执行。。。

这种方式的特点是:可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。

四、延迟连接池,newScheduledThreadPool:

public class TestScheduledThreadPoolExecutor {
 
  public static void main(String[] args) {
 
    ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
 
    exec.scheduleAtFixedRate(new Runnable() {//每隔一段时间就触发异常
 
           @Override
 
           publicvoid run() {
 
              //throw new RuntimeException();
 
              System.out.println("================");
 
           }
 
         }, 1000, 5000, TimeUnit.MILLISECONDS);
 
    exec.scheduleAtFixedRate(new Runnable() {//每隔一段时间打印系统时间,证明两者是互不影响的
 
           @Override
 
           publicvoid run() {
 
              System.out.println(System.nanoTime());
 
           }
 
         }, 1000, 2000, TimeUnit.MILLISECONDS);
 
  }
 
}

   

输出结果:

================
 
8384644549516
 
8386643829034
 
8388643830710
 
================
 
8390643851383
 
8392643879319
 
8400643939383

   

以上就是本文的全部内容,希望对大家的学习有所帮助。

更多四种Java线程池用法解析相关文章请关注PHP中文网!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn