什么是异步,什么是线程池,如何使用?

一、概述:

在 Java 中,异步是一种编程模型,它允许程序在执行某个操作时,不必等待该操作完成然后再去执行其他操作。这种方式可以提高程序的性能和响应速度。在 Java 中,异步一般是通过线程池来实现的。

线程池是一种管理和复用线程的机制,它可以在程序启动时创建一定数量的线程,并把这些线程放到一个池子里面,等待任务的到来。当任务到来时,线程池会从池子里面取出一个线程来执行任务。当任务执行完毕后,线程会被放回到池子里面,等待下一个任务的到来。线程池可以避免频繁地创建和销毁线程,从而提高程序的性能。

在 Java 中,使用线程池可以通过 Executor 框架来实现。Executor 框架提供了一系列的接口和类,用于管理线程池和提交任务。下面是一个使用线程池的示例代码:

ExecutorService executor = Executors.newFixedThreadPool(10); // 创建一个固定大小的线程池
executor.submit(new Runnable() {
    
       
	 @Override    
	 public void run() {
    
            
	 // 执行异步任务    
}});
executor.shutdown(); // 关闭线程池

在这个示例中,我们首先通过 Executors 工厂类创建了一个固定大小的线程池,然后通过 submit 方法提交了一个异步任务,最后通过 shutdown 方法关闭了线程池。

二、线程回顾

1、初始化线程的 4 种方式

1)、继承 Thread
2)、实现 Runnable 接口
3)、实现 Callable 接口 + FutureTask (可以拿到返回结果,可以处理异常)
4)、线程池

方式 1 和方式 2:主进程无法获取线程的运算结果。

public static void main(String[] args) {
    
            
    PrintThread p = new PrintThread();        
     p.start();
 }
    
public static class PrintThread extends Thread {
    
    
      public final long startTime = System.currentTimeMillis();

      public void run() {
    
    
          try {
    
    
              while (true) {
    
    
                  // 每秒打印时间信息
                  long t = System.currentTimeMillis() - startTime;
                  System.out.println(t / 1000 + "." + t % 1000);
                  Thread.sleep(1000);
              }
          } catch (Exception ex) {
    
    
              ex.printStackTrace();
          }
      }
  }

方式 3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致
服务器资源耗尽。

public class BasicThreadDemo {
    
    

    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        FutureTask<Integer> futureTask = new FutureTask<>(new CallableDemo());
        new Thread(futureTask).start();
        Integer result = futureTask.get();
        System.out.println(result);
    }


    public static class CallableDemo implements Callable{
    
    
        @Override
        public Object call() throws Exception {
    
    
            System.out.println("call..............");
            return null;
        }
    }
}


方式 4:通过如下两种方式初始化线程池

Executors.newFiexedThreadPool(3);
//或者
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit unit, workQueue, threadFactory,

通过线程池性能稳定,也可以获取执行结果,并捕获异常。但是,在业务复杂情况下,一
个异步调用可能会依赖于另一个异步调用的执行结果。

三、线程池详解

1 线程池的七大参数

@param corePoolSize the number of threads to keep in the pool, even
if they are idle, unless {@code allowCoreThreadTimeOut} is set
池中一直保持的线程的数量,即使线程空闲。除非设置了 allowCoreThreadTimeOut

@param maximumPoolSize the maximum number of threads to allow in the pool
池中允许的最大的线程数

@param keepAliveTime when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.
当线程数大于核心线程数的时候,线程在最大多长时间没有接到新任务就会终止释放,最终线程池维持在 corePoolSize 大小

@param unit the time unit for the {@code keepAliveTime} argument
时间单位

@param workQueue the queue to use for holding tasks before they are > executed.
This queue will hold only the {@code Runnable} tasks submitted by the {@code execute} method.
阻塞队列,用来存储等待执行的任务,如果当前对线程的需求超过了 corePoolSize
大小,就会放在这里等待空闲线程执行。

@param threadFactory the factory to use when the executor creates a new thread
创建线程的工厂,比如指定线程名等

@param handler the handler to use when execution is blocked because the thread bounds and queue capacities are reached
拒绝策略,如果线程满了,线程池就会使用拒绝策略。

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

2 运行流程:

1、线程池创建,准备好 core 数量的核心线程,准备接受任务
2、新的任务进来,用 core 准备好的空闲线程执行。

(1) 、core 满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队
列获取任务执行
(2) 、阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
(3) 、max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自
动销毁。最终保持到 core 大小
(4) 、如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策
略进行处理

3、所有的线程创建都是由指定的 factory 创建的。

3 面试题:

一个线程池 core 7; max 20 ,queue:50,100 并发进来怎么分配的;
先有 7 个能直接得到执行,接下来 50 个进入队列排队,在多开 13 个继续执行。现在 70 个
被安排上了。剩下 30 个默认拒绝策略。

4 常见的 4 种线程池

  • newCachedThreadPool
     创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若
    无可回收,则新建线程。

  • newFixedThreadPool
     创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

  • newScheduledThreadPool
     创建一个定长线程池,支持定时及周期性任务执行。

  • newSingleThreadExecutor
     创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务
    按照指定顺序(FIFO, LIFO, 优先级)执行。

四、开发中为什么使用线程池及优缺点

1 开发中为什么使用线程池

  • 1 降低资源的消耗
    通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
  • 2 提高响应速度
    因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务
    的状态,当任务来时无需创建新的线程就能执行
  • 3 提高线程的可管理性
    线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来
    的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使
    用线程池进行统一分配

2 优缺点

优点:

  • 控制并发数量:线程池可以限制同时运行的线程数量,避免因线程数量过多而导致系统资源耗尽的问题。这有助于提高系统的稳定性和可靠性。

  • 减轻上下文切换负担:线程池中的线程已经预先创建好,并处于就绪状态。当需要执行任务时,只需从线程池中获取一个线程,而无需重新创建和启动新线程。这样可以减少上下文切换的开销,提高程序的执行效率。

  • 提高响应速度:由于线程池中的线程已经预先创建好并处于就绪状态,因此任务提交后可以立即执行,提高了程序的响应速度。

  • 管理资源:线程池负责管理线程的创建、销毁和复用,有助于降低系统资源消耗,提高程序的可维护性。

缺点:

  • 调度延迟:由于线程池会缓存一定数量的线程,如果任务队列很长,那么新任务可能会被推迟一段时间才能执行。这可能导致任务执行的顺序不是严格按顺序进行的。

  • 内存占用:线程池需要维护一定数量的线程,这会增加内存占用。在高负载情况下,可能会导致系统内存不足的问题。

  • 适应性问题:不同的任务可能需要不同类型和数量的线程来执行。如果没有合适的设置线程池的大小或任务队列的优先级,可能会导致性能瓶颈或资源浪费。

总之,线程池是一种有效的管理和复用线程的方法,可以在一定程度上提高程序的性能和稳定性。然而,在使用线程池时也需要注意其潜在的缺点,如调度延迟、内存占用等。

五、源码下载

gitee.com/charlinchenlin/koo-erp

猜你喜欢

转载自blog.csdn.net/lovoo/article/details/130843068