线程和线程池

多线程编程是业务开发中要用到的一项技术,尽管面临着一些挑战,但多线程开发也有着很多优点,例如资源利用率更好,程序设计在某些情况下更简单,程序响应更快等等。接下来主要说一下线程的几种创建方式以及线程池的应用。

1通过继承Thread的方式来创建线程


2通过实现Runnable接口来创建线程


这两种方式本质上其实是一样的,都是通过run()方法,深挖源码的话,第一种方式中Thread其实也是实现了Runnable接口,然后重写run()方法。然而这两种方式有一个不足,就是run()方法里并不能返回一个结果和抛出异常,所以就有了第三种创建线程的方式,通过Callable接口,并用FutureTask来接收返回的对象。

3通过Callable接口,并用FutureTask来接收返回的对象


和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。call()方法可以有返回值,call()方法可以声明抛出异常

Java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口,因此可以作为Thread类的target。在Future接口里定义了几个公共方法来控制它关联的Callable任务。

接下来说线程池,线程池是管理线程的地方,在一个项目中,我们知道需要多次的创建线程和销毁线程,然而创建和销毁是及其耗费内存资源的,所以就有了线程池的需要,来帮我们管理调度线程,省下了很多的内存资源。

我们应该如何创建一个线程池那?Java中已经提供了创建线程池的一个类:Executor

而我们创建时,一般使用它的子类:ThreadPoolExecutor。但是从ThreadPoolExecutor到Executor并不是简单的实现或继承关系,两者中间有好多中间类。下面是各类之间的层级关系调用实现类图


线程池类图

我把这几个类用黑色线条画了起来,发现我们需要的ThreadPoolExecutor首先继承了抽象类AbstractExecutorService,然后AbstractExecutorService实现了ExecutorService接口,最ExecutorService接口又实现了Executor接口。

我们再来看一下ThreadPoolExecutor构造函数的参数


我们可以看出,线程池中的corePoolSize就是线程池中的核心线程数量,这几个核心线程,只是在没有用的时候,也不会被回收,maximumPoolSize就是线程池中可以容纳的最大线程的数量,而keepAliveTime,就是线程池中除了核心线程之外的其他的最长可以保留的时间,因为在线程池中,除了核心线程即使在无任务的情况下也不能被清除,其余的都是有存活时间的,意思就是非核心线程可以保留的最长的空闲时间,而util,就是计算这个时间的一个单位,workQueue,就是等待队列,任务可以储存在任务队列中等待被执行,执行的是FIFIO原则(先进先出)。threadFactory,就是创建线程的线程工厂,最后一个handler,是一种拒绝策略,我们可以在任务满了之后,拒绝执行某些任务,具体的拒绝策略如下:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。

ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

再来看线程池的执行流程


线程池的执行流程

我们可以看出,任务进来时,首先执行判断,判断核心线程是否处于空闲状态,如果不是,核心线程就先就执行任务,如果核心线程已满,则判断任务队列是否有地方存放该任务,若果有,就将任务保存在任务队列中,等待执行,如果满了,在判断最大可容纳的线程数,如果没有超出这个数量,就开创非核心线程执行任务,如果超出了,就调用handler实现拒绝策略。

下面一段测试代码来说明这个线程池执行的流程


最后运行结果与执行流程是一样的,都是先判断核心线程,当核心线程最大后,在判断队列,当队列也满后,在进入非核心线程。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android中的线程 线程,在Android中是非常重要的,主线程处理UI界面,子线程处理耗时操作。如果在主线程...
    shenhuniurou阅读 779评论 0 3
  • 不同形式的线程虽然都是线程,但是它们仍然具有不同的特性和实用场景: AsyncTask封装了线程池和Handler...
    胡二囧阅读 1,135评论 0 4
  • 一.Java中的ThreadPoolExecutor类 java.uitl.concurrent.ThreadPo...
    谁在烽烟彼岸阅读 657评论 0 0
  • 导语 在Android系统,线程主要分为主线程和子线程,主线程处理和界面相关的事情,而子线程一般用于执行耗时操作。...
    一个有故事的程序员阅读 609评论 0 3
  • 1 你的样子跟别人不同。 每次点开运动的app,总是看到圈内的人都在晒各种健身后的照片或撩腹部的照片。不记得听谁说...
    听风想雨阅读 321评论 5 1