• java线程池


    一、创建线程的三种方式

    1、通过继承Thread类继承

      

    2、通过Runnable接口,重写run方法创建

    3、通过Callable接口,实现call方法的创建

    4、使用线程池的方式创建

    二、为什么要用线程池

    线程池提供了一种限制和管理资源(包括执行一个任务)的方式,每一个线程池都维护了一些基本的统计信息,例如已完成的任务数量。

    线程池的优点:

    1、降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成消耗

    2、提高响应速度,当任务到大时,任务可以不需要等到线程创建就能够立即执行。

    3、提高线程的可管理性。线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程可以统一分配和调优监控。

    三、实现Runnable接口和Callable接口的区别

    Runnable自1.0以来一直存在,但callable仅在jdk1.5引入,目的是为了处理runnable不支持的用例。

    runnbale接口不会返回结果或抛出检查异常,但是callable可以。

    所以如果任务不需要返回结果和抛出异常,推荐使用runnable接口,这样代码会更简洁。

    四、线程池实现的两种方法(1、ThreadPoolExecutor构造方法,2、使用Executor框架的工具类Executors来实现)

    第一种ThreadPoolExecutor 三个重要参数:

    1、corePoolSize:核心线程数定义了最小可以同时运行的线程数

    2、maximumPoolSize:当前队列存放的任务到大队列容量的时候,当前程序可以同时运行的线程数量最大的线程数

    3、workQueue:当前任务队列。当前任务来了判断是否还有线程能够处理,如果不能就进入任务队列

    四个其他参数:

    4、keepAlive:当前线程数量大于corePoolSize的时候,如果这个时候没有任务提交,核心线程外的线程不会立即销毁,而是等待,时间超过了就会被销毁。

    5、unit:参数 的时间单位

    6、threadFactory:executor创建新线程的时候会用到

    7、handler:饱和拒绝策略。

    饱和策略定义:

    如果当前同时运行的线程数量达到最大线程数量,并且队列已被放满的时候,ThreadPoolTaskExecutor定义了一些策略:

    1、ThreadPoolExecutor.AbortPolicy:抛出RejectedExecutionException来拒绝新任务的处理

    2、ThreadPoolExecutor.CallerRunsPolicy:调用执行自己的线程运行任务,也就是直接调用execute (让调用者自己去执行任务,非线程池中的线程执行)。

    方法的线程中运行run被拒绝的任务,如果执行程序已关闭,则丢弃该任务。因此这种策略降低了新任务的提交速度

    影响程序的整体性能。如果程序可以承受这种延迟,可以选择该策略。

    3、ThreadPoolExecutor.DiscardPolicy:不处理新任务,直接丢弃

    4、ThreadPoolExecutor.DiscardOldestPolicy:此策略将丢弃最早未处理的请求。

    阿里巴巴推荐中:不允许使用Executors创建线程,而是通过ThreadPoolExecutor的方式,这样处理方式规避资源消耗风险,明确线程池的运行规则。

    弊端主要还是回可能造成OOM

    FixedThreadPool和SingleThreadExecutor:允许请求队列的长度为Integer.MAX_VALUE,可能堆积大量的请求,导致OOM

    CacheThreadPool和ScheduledThreadPool:允许创建线程数量为Integer.MAX_VALUE,可能会创建大量线程,导致OOM

    execute()VS     submit()

    1、execute方法用于提交不需要返回值的任务,所以无法判断任务是否成功被执行。

    2、submit方法用于提交需要返回值的任务,线程池会返回一个future类型的对象通过future这个对象可以判断任务是否执行成功,并且future的get方法获得,t并且get中可以指定时间参数,imeout时间内没有执行完,会报异常。

    shutdown vs shtdownnow

    1、shutdown关闭线程池,线程池的状态变为shutdown,线程池不再接受新任务,但是任务队列会执行完

    2、关闭线程池,线程状态变为STOP,线程池会终止当前正在运行的任务,并停止处理排队的任务,并返回正在等待执行的list。

    isTermimated()VS isShutDown()

    1、isTerminated当调用shutdown方法后,并且所欲偶提交的任务完成后,返回为true

    2、isshutdown当调用shutdown后返回true。

    注意:建议不同类别的业务使用不同的线程池,配置线程池的时候根据当前业务的情况对当前线程进行配置,因为不同的业务的并发对资源使用情况不同。

    并且可能会造成死锁现象。

    假如父任务等待子任务完成获取锁,但是父任务占据了所有的线程数,那么子任务就会一直阻塞在阻塞队列中。

    注意:线程池数量太小会造成,任务队列等待任务过多,可能会造成OOM

         线程池数量太大会造成,线程创建过多,导致CPU上下文切换过于密集,影响整体执行效率

    创建线程数量的常用经验公式:

      1、CPU密集型(N+1):这种任务消耗的主要是CPU资源,比CPU核心数都一个线程,防止线程偶发的缺页中断,或者其他原因导致任务暂停。

    一旦任务暂停了,CPU就会空闲,可以让下一个线程来处理任务。

      2、I/O密集型,这种任务系统大部分时间会用来处理I/O请求,而线程在IO阶段不会占CPU时间,因此I/O密集型任务可以多配置一些线程,2N来占据CPU。(某些线程会被暂停去IO,所以可以让更多的线程来占领CPU资源)

    面试题:

    为什么不建议使用Executor去创建线程池。

    FixedThreadPool和SingleThreadExecutor:允许请求队列长度为Integer.MAX_VALUE,可能堆积大量的请求,导致OOM

    CachedThreadPool和ScheduleThreadPool:允许创建线程数量为Integer.MAX_VALUE,可能会创建大量线程,导致OOM

    除了避免OOM原因以外:

    1、实际使用中需要根据自己机器性能、业务场景来手动配置线程的参数比如核心线程数、任务队列、饱和策略等,显示地给线程池命有助于定位。】

    线程池的执行流程:

    1、线程池被创建后里面有线程吗?

    线程池被创建后里面是没有线程的,如果需要预热可以调用线程池的方法prestartAllCoreThreads等方法。

    2、核心线程数会被回收吗?

    核心线程数是不会被回收的,如果需要回收核心线程还是可以调用方法。

    3、

  • 相关阅读:
    使用WPF Tree Visualizer 查看popup的Dialog
    用代码控制build,相关资料搜集
    AreComObjectsAvailableForCleanup and CleanupUnusedObjectsInCurrentContext
    window size in Windows User Experience Interaction Guidelines
    有关PFIF
    应用程序挂起的秘密
    How do I determine if a WPF window is modal?
    Associate extension with c# exe
    提升WPF的启动速度
    Marshal.ReleaseComObject
  • 原文地址:https://www.cnblogs.com/Alei777/p/16227657.html
Copyright © 2020-2023  润新知