文章      动态     相关文章     最新文章     手机版动态     相关动态     |   首页|会员中心|保存桌面|手机浏览

0ok95r

http://keair.bhha.com.cn/com0ok95r/

相关列表
文章列表
  • 暂无文章
推荐文章
联系方式
  • 联系人:王女士
  • 电话:13603206949
java多线程和单线程的具体表现
发布时间:2024-12-13        浏览次数:13        返回列表


java多线程和单线程的具体表现

目录

1.线程池的优点:    

2.线程池的实现原理:

2.1 ThreadPoolExecutor执行示意图

3.线程池的使用

4.向线程池提交任务

5.关闭线程池

6.线程池提供方法可以重写

7.监控线程池,自定义线程池


1.线程池的优点:    
        线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用。可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃。

2.线程池的实现原理:
        提交一个任务到线程池中,线程池的处理流程如下:

     1> 判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。

    2> 线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。

   3> 判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

 2.1 ThreadPoolExecutor执行示意图

 上图说明:

1、如果线程池中的线程数量少于corePoolSize,就创建新的线程来执行新添加的任务
2、如果线程池中的线程数量大于等于corePoolSize,但队列workQueue未满,则将新添加的任务放到workQueue中
3、如果线程池中的线程数量大于等于corePoolSize,且队列workQueue已满,但线程池中的线程数量小于maximumPoolSize,则会创建新的线程来处理被添加的任务
4、如果线程池中的线程数量超出了maximumPoolSize,就用RejectedExecutionHandler.rejectedExecution()方法来执行拒绝策略

        总结:ThreadPoolExecutor采取上述的总体设计思路,就是为了在执行execute方法时,尽可能地避免获取全局锁,当运行的线程数大于等于corePoolSize时,几乎所有的excute()都会执行步骤2,而步骤2而不需要获取全局锁,步骤3是需要加锁创建的。

源码:

3.线程池的使用
            我们可以通过ThreadPoolExecutor来创建线程池

ThreadPoolExecutor有多个构造器,不一定全部参数都包含,没有包含的使用默认值,以下是参数说明:

  •   corePoolSize:核心线程池的线程数量
  •   maximumPoolSize:最大的线程池线程数量
  •   keepAliveTime:线程活动保持时间,线程池的工作线程空闲后,保持存活的时间,一般都是0
  •   unit:线程活动保持时间的单位。
  •         workQueue:指定任务队列所使用的阻塞队列,当然不仅仅只有下面三个阻塞队列,有优先级的可以考虑PriorityBlockingQueue,建议使用有界队列,否则后面无界的话不停的创建线程,容易撑爆内存
  • ThreadFactory:线程工厂,主要用来创建线程;
  •       handler:当队列和线程池都满了,说明线程池处于饱和状态,那么必须对新提交的任务采用一种特殊的策略来进行处理,默认是AbortPolicy,这四个都是ThreadPoolExecutor的静态内部类,这几个类都是是实现RejectedExecutionHandler接口的hanler有以下四种取值:

我们现在用第四种策略来处理上面的程序例子:

执行结果:

线程池中活跃的线程数: 1
线程池中活跃的线程数: 2
线程池中活跃的线程数: 2
----------------队列中阻塞的线程数1
线程池中活跃的线程数: 2
----------------队列中阻塞的线程数2
线程池中活跃的线程数: 2
----------------队列中阻塞的线程数3
线程池中活跃的线程数: 3
----------------队列中阻塞的线程数3
线程池中活跃的线程数: 4
----------------队列中阻塞的线程数3
线程池中活跃的线程数: 5
----------------队列中阻塞的线程数3
线程池中活跃的线程数: 5
----------------队列中阻塞的线程数3

        这里采用了丢弃策略后,就没有再抛出异常,而是直接丢弃。在某些重要的场景下,可以自定义处理策略,采用记录日志或者存储到数据库中,而不应该直接丢弃。

4.向线程池提交任务
          有两个方法提交,一个是execute()方法和submit()方法。

ThreadPoolExecutor中实现的execute实现的方法见上面所示,接口Exeutor中定义如下:

 AbstractExecutorService中的submit方法,有三种重载方法,源码:

5.关闭线程池
    可以通过调用线程池的shutdown或者shutdownNow方法关闭线程池,他们的原理都是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止,两者的差别:

  •         shutdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或者暂停任务的线程,并返回等待执行任务的列表;
  •         shutdown只是将线程池的状态设置为SHUTDOWN状态,然后中断所有没有正在执行任务的线程。

    只要调用了这两个关闭方法的任意一个,isShutDown方法就会返回true,当所有的任务都已经关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true,至于应该调用那一个方法,可以根据线程池的特性来决定,通常用shutdown方法调用关闭线程池,如果任务不一定执行完,则可以调用shutdownNow。

6.线程池提供方法可以重写
     这几个是ThreadPoolExecutor提供的未实现的方法,可以通过重写他们在任务执行前,执行后和线程池关闭前执行一些代码来进行监控。

7.监控线程池,自定义线程池

        一般我们创建线程池的时候,有两种选择,第一种是通过ThreadPoolExecutor创建,还有一种就是通过Executors创建。那么他们区别是什么呢,我们接下来分析:

        Executors类和ThreadPoolExecutor都是util.concurrent并发包下面的类, Executos下面的newFixedThreadPool、newScheduledThreadPool、newSingleThreadExecutor、newCachedThreadPool底线的实现都是用的ThreadPoolExecutor实现的,所以ThreadPoolExecutor更加灵活。

 ThreadPoolExecutor

 Executors

线程池通过Executors类的四种创建方式

  • Java通过Executors提供四种线程池,分别为:
  • newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  • newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
  • newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

一、newCachedThreadPool:由参数可以看出, 核心线程数为0,最大线程数很大,队列为无界阻塞队列,空闲存活时间为60秒,意思就是说,来个任务,如果没有空闲的线程,就创建个线程,如果有空闲的线程,就要空闲的线程。

二、newFixedThreadPool:由参数可以看出,这是创建一个固定大小的线程池,核心线程数和最大线程数一致,linkedBlockingQueue为无界阻塞队列,意思就是说我最多只能创建固定大小的线程来处理任务,比如核心线程数和最大线程数都为7,但突然来了12个任务,另外5个无法立刻执行,就放到了linkedBlockingQueue中,如果内存无限大,任务可以是无限多的,newFixedThreadPool中的keepAliveTime、unit都是无意思的,原因最大线程数没有超过核心线程数。

三、newScheduledThreadPool:由参数可以看出, 核心线程数一定,最大线程数很大,队列为优先级队列此线程池可以做定时执行。

三、newSingleThreadExecutor:由参数可以看出, 核心线程数和最大线程数都是1,linkedBlockingQueue为无界阻塞队列,也就是说是中只有一个线程在执行,来了再多个任务都得等,等线程执行完上个任务,再去执行下个任务。

自定义ThreadPoolExecutor

有界队列:根据上面参数可知,当队列为有界队列时,

corePoolSize < maximumPoolSize

corePoolSize < maximumPoolSize ,BlockingQueue满了,线程池会创建新的线程,直到maximumPoolSize 没满,没有触发拒绝策略,过了一段时间,任务减少了,一些线程空闲时间超过了keepAliveTime,就会被移除。                                                         

corePoolSize = maximumPoolSize

无界队列:

corePoolSize < maximumPoolSize,队列一直不会满(理想情况下),不会触发拒绝策略,如果一段时间没有任务时线程闲置时间超过keepAliveTime,会被移除线程池。