why:利用多线程技术可以使系统同时运行多个程序块,缩短出程序响应的时间,提高计算机资源的利用率,达到多任务处理的目的
how:
1、继承Thread类或者实现Runnable接口
如果想有返回值:实现Callable接口,可以获取Future对象
2、线程池(推荐使用)
好处:省去创建和销毁时间,节约服务器资源,提升执行效率。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题
阿里公约推荐使用 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:
1)FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2)CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
示例:
@Configuration public class ThreadPool { private static volatile ThreadPoolExecutor threadExecutorPool; @Bean(name = "threadExecutorPool") public ThreadPoolExecutor getImportCadExecutorPool() { if (threadExecutorPool == null) { synchronized (ThreadPool.class) { if (threadExecutorPool == null) { threadExecutorPool = new ThreadPoolExecutor( 10, //核心线程数 10, //最大线程数 60, //空闲线程存活时间 TimeUnit.SECONDS, //时间单位 new LinkedBlockingQueue<Runnable>(1024), //线程池所使用的缓冲队列 new NamedThreadFactory("pool_name", false), //线程池创建线程使用的工厂,此处是线程池名称 new ThreadPoolExecutor.CallerRunsPolicy()); //线程池对拒绝任务的处理策略 } } } return threadExecutorPool; } }
执行逻辑说明:
-
判断核心线程数是否已满,核心线程数大小和corePoolSize参数有关,未满则创建线程执行任务
-
若核心线程池已满,判断队列是否满,队列是否满和workQueue参数有关,若未满则加入队列中
-
若队列已满,判断线程池是否已满,线程池是否已满和maximumPoolSize参数有关,若未满创建线程执行任务
-
若线程池已满,则采用拒绝策略处理无法执执行的任务,拒绝策略和handler参数有关
线程池拒绝策略:
1、AbortPolicy
直接丢弃任务,抛出RejectedExecutionException异常,是默认策略
2、CallerRunsPolicy
只用调用者所在的线程处理任务
3、DiscardOldestPolicy
丢弃等待队列中最旧的任务,并执行当前任务
4、DiscardPolicy
直接丢弃任务,但不抛出异常
3、java8新特性 parallelStream parallelStream的底层是基于ForkJoinPool线程池 Fork/Join框架主要采用分而治之的理念来处理问题,对于一个比较大的任务,首先将它拆分(fork)为两个小任务task1与task2,最后将这两个任务结果合并 ForkJoinPool线程池中是异步执行,且队列长度为AXIMUM_QUEUE_CAPACITY = 1 << 26 //67 108 864位,其更适合CPU密集型的任务使用。IO型任务容易OOM 使用: 推荐通过此方式获取一个公用线程池线程池 ForkJoinPool forkJoinPool = ForkJoinPool.commonPool(); 可使用 System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","20");设置并行数 也可以直接new ForkJoinPool();
关于线程安全:
1)常量始终是线程安全的,因为只存在读操作。
2)每次调用方法前都新建一个实例是线程安全的,因为不会访问共享的资源。
3)局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量和方法内变量。