写在前面

学习多线程,有几个很关键的点,要提前预习

  • 几个关键字
    volatile
    synchronized
    final
    static

  • Thread基本方法

多线程编程方式是由 ExecutorService 发起操作的,其实操作的对象,还是基于线程,关于线程/job/task的颗粒度,以及这里ExecutorService中线程池的概念,都是要加强学习的

这里先对 ExecutorService 的几个方法,简介一下

一、线程基础 Thread

参照这里Thread的基本知识介绍。

一、基本方法介绍

1.1、shutdown()

  • 不能接受新的submit
  • 并没有任何的interrupt操作,会等待线程池中所有线程(执行中的以及排队的)执行完毕

可以理解为是个标识性质的方法,标识这程序有意愿在此刻终止线程池的后续操作。

1.2、shutdownNow()

  • 会尝试interrupt线程池中正在执行的线程
  • 等待执行的线程也会被取消
  • 但是并不能保证一定能成功的interrupt线程池中的线程。
  • 会返回并未终止的线程列表List

shutdownNow()方法比shutdown()强硬了很多,不仅取消了排队的线程而且确实尝试终止当前正在执行的线程。

1.3、awaitTermination(n, TimeUnit)

  • 该方法返回值为boolean类型
  • 方法的两个参数规定了方法的阻塞时间,在阻塞时间内除非所有线程都执行完毕才会提前返回true
  • 如果到了规定的时间,线程池中的线程并没有全部结束返回false
  • InterruptedException 这个异常也会导致方法的终止

利用这个阻塞方法的特性,我们可以优雅的关闭线程池中的任务。

Java中在使用Executors线程池时,有时场景需要主线程等各子线程都运行完毕后再执行。这时候就需要用到ExecutorService接口中的awaitTermination方法.

比如应用场景为线程池的有效执行时间为20S,20S之后不管子任务有没有执行完毕,都要关闭线程池。代码如下:

	ExecutorService es = Executors.newFixedThreadPool(10);
        es.execute(new Thread());//执行子线程任务
        try {
   
		es.shutdown();  
		if(!es.awaitTermination(20,TimeUnit.SECONDS)){
   //20S
		   System.out.println(" 到达指定时间,还有线程没执行完,不再等待,关闭线程池!");
		   es.shutdownNow();  
		}
	} catch (Throwable e) {
   
		// TODO Auto-generated catch block
		es.shutdownNow();
		e.printStackTrace();
	}

与shutdown()方法结合使用时,尤其要注意的是shutdown()方法必须要在awaitTermination()方法之前调用,该方法才会生效。否则会造成死锁。

1.4、submit()

重载了三个submit方法,接收入参Runnable,Callable

1.5、invokeAll

任务的批量提交invokeAll(),两个重载

1.6、invokeAny

方法invokeAny,invokeAll都具有阻塞性。

invokeAny取得第一个方法的返回值,当第一个任务结束后,会调用interrupt方法中断其它任务。
invokeAll等线程任务执行完毕后,取得全部任务的结果值。

1.7、isTerminated()

若关闭后所有任务都已完成,则返回true。注意除非首先调用shutdown或shutdownNow,否则isTerminated永不为true。返回:若关闭后所有任务都已完成,则返回true。

二、操作使用

2.1、选择合适的线程池

有如下常见的几种线程池的构建,区分场景

newFixedThreadPool ----->
newWorkStealingPool -----> 使用场景:
newSingleThreadExecutor -----> 使用场景:
newCachedThreadPool -----> 使用场景:
newScheduledThreadPool ----->
new ThreadPoolExecutor(…) -----> 使用场景:自适应

2.2、newFixedThreadPool

定义:定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
使用场景:任务量比较固定但耗时长的任务

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

2.3、newWorkStealingPool

定义:工作窃取线程池,创建一个拥有多个任务队列(以便减少连接数)的线程池
使用场景:会创建一个含有足够多线程的线程池,来维持相应的并行级别,它会通过工作窃取的方式,使得多核的 CPU 不会闲置,总会有活着的线程让 CPU 去运行。


public static ExecutorService newWorkStealingPool() {
   
    return new ForkJoinPool(Runtime.getRuntime().availableProcessors(),
                                                 ForkJoinPool.defaultForkJoinWorkerThreadFactory,
                                                 null, true);
}

newWorkStealingPool虽是1.8才有的,但 ForkJoinPool 是1.7就有的对象。

2.4、newSingleThreadExecutor

定义:单线程池,只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
使用场景:多个任务顺序执行(FIFO,优先级)

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
   
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }

装饰者模式

2.5、newCachedThreadPool

  • 定义:缓冲线程池,一个可根据需要创建新线程的线程池,如果现有线程没有可用的,则创建一个新线程并添加到池中,如果有被使用完但是还没销毁的线程,就复用该线程。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。

  • 使用场景:任务量大但耗时少的任务

     public static ExecutorService newCachedThreadPool() {
   
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

2.6、newScheduledThreadPool

定义:调度线程池
使用场景:定时以及周期性执行任务

 public ScheduledThreadPoolExecutor(int corePoolSize) {
   
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
   
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

2.7、ThreadPoolExecutor

定义:自定义线程池
使用场景:自定义线程池,一般不需要

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
   
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

以上默认的几种都是 JDK本身基于自定义线程池ThreadPoolExecutor构造的应对不同场景的线程池,实际使用中,缓冲线程池和定长线程池用的较多(基本应付我们遇到的场景)

三、使用