• 1)初始化 线程 的四种方式:

a:继承 Thread 类
b:实现 Runnable 接口
c:实现 Callable 接口 + FutureTask 类(可以拿到 返回结果,可以处理异常)
d:线程池:

1.实际开发中,以上三种,线程启动的方式都不使用,将所有的多线程异步任务,都交给线程池执行。
2.整个系统中,线程池只有一两个。每个异步任务,直接提交给 线程池,让他自己去执行就行。
3.代码示例:

public class ThreadTest01 {
 
    public static ExecutorService executorService = Executors.newFixedThreadPool(10);
 
    public static void main(String[] args) throws Exception {
        executorService.execute(new Thread02());
    }
}
 
class Thread02 implements Runnable {
 
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
  • 2)线程池详解
public ThreadPoolExecutor(
      int corePoolSize,
      int maximumPoolSize,
      long keepAliveTime,
      TimeUnit unit,
      BlockingQueue<Runnable> workQueue,
      ThreadFactory threadFactory,
      RejectedExecutionHandler handler) {}
 
corePoolSize     –核心线程数,创建好以后,一直存在,除非设置了allowCoreThreadTimeOut
maximumPoolSize  –池中允许的最大线程数
keepAliveTime    –存活时间,当线程数大于核心数时,只要线程空闲大于指定的 存活时间,就会释放空闲(核心线程之外的)的线程。
unit             – keepAliveTime 参数的时间单位
workQueue        –阻塞队列,如果任务有很多,就会将 目前多的任务,放在队列里面,只要有线程空闲,就回去队列取出新的任务执行。
threadFactory    –创建线程的工厂(默认)
handler          –如果队列满了,按照 我们指定的 拒绝策略 拒绝执行任务。

例如,实际生产项目中的线程池使用:

@Bean(name = "bizLogicTaskExecutor", destroyMethod = "shutdown")
	public Executor bizLogicTaskExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(10);
		executor.setMaxPoolSize(30);
		executor.setQueueCapacity(200);
		executor.setKeepAliveSeconds(60);
		executor.setThreadNamePrefix("Biz_Logic_");
		// 设置等待所有任务执行结束再关闭线程池
		executor.setWaitForTasksToCompleteOnShutdown(true);
		// 该方法用来设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,
		// 以确保应用最后能够被关闭,而不是阻塞住
		executor.setAwaitTerminationSeconds(60);

		// rejection-policy:当pool已经达到max size的时候,如何处理新任务
		// CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
		executor.initialize();
		return executor;
	}
  • 线程池的参数怎么配置才能达到系统最佳的效果呢?

首先,需要考虑到线程池所进行的工作的性质:
IO密集型
CPU密集型

简单的分析来看,如果是CPU密集型的任务,我们应该设置数目较小的线程数。如果是IO密集型的任务,则应该设置可能多的线程数。如果线程数目太多,那么线程切换所带来的开销又会对系统的响应时间带来影响。
1.CPU密集型

对于 CPU 密集型计算,多线程本质上是提升多核 CPU 的利用率,所以对于一个 8 核的 CPU,每个核一个线程,理论上创建 8
个线程就可以了。如果设置过多的线程数,实际上并不会起到很好的效果。此时假设我们设置的线程数量是 CPU 核心数的 2
倍,因为计算任务非常重,会占用大量的 CPU 资源,所以这时 CPU
的每个核心工作基本都是满负荷的,而我们又设置了过多的线程,每个线程都想去利用 CPU
资源来执行自己的任务,这就会造成不必要的上下文切换,此时线程数的增多并没有让性能提升,反而由于线程数量过多会导致性能下降。因此,对于 CPU
密集型的计算场景,理论上线程的数量 = CPU 核数就是最合适的,不过通常把线程的数量设置为CPU 核数
+1,会实现最优的利用率。即使当密集型的线程由于偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程也能确保 CPU 的时钟周期不会被浪费,从而保证 CPU 的利用率。

2.IO密集型

对于 IO 密集型任务最大线程数一般会大于 CPU 核心数很多倍,因为 IO 读写速度相比于 CPU 的速度而言是比较慢的,如果我们设置过少的线程数,就可能导致 CPU 资源的浪费。而如果我们设置更多的线程数,那么当一部分线程正在等待 IO 的时候,它们此时并不需要 CPU 来计算,那么另外的线程便可以利用 CPU 去执行其他的任务,互不影响,这样的话在任务队列中等待的任务就会减少,可以更好地利用资源。对于 IO 密集型计算场景,最佳的线程数是与程序中 CPU 计算和 IO 操作的耗时比相关的,《Java并发编程实战》的作者 Brain Goetz 推荐的计算方法如下:
线程数 = CPU 核心数 * (1 + IO 耗时/ CPU 耗时)