博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
线程和线程池
阅读量:7099 次
发布时间:2019-06-28

本文共 2872 字,大约阅读时间需要 9 分钟。

可从以下几个方面入手,学习线程和线程池:

1、什么是线程,线程和进程的区别是什么;

2、线程中的基本概念,线程的生命周期;

3、单线程和多线程;

4、线程池的原理解析;

5、常见的几种线程池的特点以及各自的应用场景

 

1、线程,程序执行流的最小执行单位,是实际运作的单位。

进程是一个动态的过程,是一个活动的实体。

一个应用程序的运行就可以被看做是一个进程,而线程,是运行中的实际的任务执行者。

可以说,进程中包含了多个可以同时运行的线程。

 

2、线程的生命周期可利用下图理解:

 

 

第一步,用new Thread()的方法新建一个线程,在线程创建完成之后,线程就进入了就绪(Runnable)状态,此时创建出来的线程进入抢占CPU资源的状态,当线程抢到了CPU的执行权之后,线程就进入了运行状态(Running),当该线程的任务执行完成之后或者是非常态的调用stop()方法之后,线程就进入了死亡状态。而我们在图解中可以看出,线程还具有一个阻塞的过程。当面对下面几种情况的时候,容易造成线程阻塞,第一种,当线程主动调用了sleep()方法时,线程会进入阻塞状态,除此之外,当线程中主动调用了阻塞的IO方法时,这个方法有一个返回参数,当参数返回之前,线程也会进入阻塞状态,还有一种情况,当线程进入正在等待某个通知时,会进入阻塞状态。阻塞状态出现的原因:CPU资源十分宝贵,因此,当线程正在进行某种不正确时长的任务时,java就会收回CPU的执行权,从而合理应用CPU的资源。从上图可以看出,线程在阻塞过程结束后,会重新进入就绪状态,重新抢夺CPU资源。那么如何跳出阻塞过程呢?以上几种可能造成线程阻塞的情况,都存在一个时间限制,当sleep()方法的睡眠时长过去后,线程就自动跳出了阻塞状态;第二种则是在返回了一个参数之后,在获取到了等待的通知时,就自动跳出了线程的阻塞过程。

 

3、

单线程,顾名思义既是只有一条线程在执行任务,这种情况在工作、学习中较少;

多线程,创建多条线程同时执行任务,这种方式较常见。但是,在多线程的使用过程中,需了解其他概念。比如,理解并行和并发的区别;实际应用过程中多线程的安全问题。

并行和并发:

并发,从宏观方面来说,并发就是同时进行多种事情,实际上,这几种事情,并不是同时进行的,而是交替进行的,而由于CPU的运算速度非常快,会造成一种错觉,就是在同一时间内进行了多种事情。

并行,真正意义上的同时进行多种事情。这种只可以在多核CPU的基础下完成。

 

为什么会造成多线程安全问题呢?如果多个线程同时执行一个任务,那么意味着他们共享同一种资源,由于线程CPU的资源不一定可以被谁抢占到,这时,第一条线程先抢占到CPU资源,他刚刚进行了第一次操作,而此时第二条线程抢占到了CPU的资源,那么,共享资源还来不及发生变化,就同时有两条数据使用了同一条资源,这个问题如何解决?

由造成问题的原因可以看出,这个问题的主要矛盾在于,CPU的使用权抢占和资源的共享发生了冲突,解决时,只需要让一条线程抢占了CPU的资源时,阻止第二条线程同时抢占CPU的执行权,在代码中,只需要在方法中使用同步代码块即可。

 

4、由以上的介绍可以看出,在一个应用程序中,需要多次使用线程,也就意味着,需要多次创建并销毁线程。而创建并销毁线程的过程势必会消耗内存。而在java中,内存资源是极其宝贵,所以,提出了线程池的概念。

线程池:java中开辟出了一种管理线程的概念,这个概念叫做线程池,从概念以及应用场景中,可以看出,线程池的好处,就是可以方便的管理线程,也可以减少内存的消耗。

如何创建一个线程池?java中已经提供了创建线程池的一个类,Executor

而创建时,一般使用它的子类:ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,

               int maximumPoolSize,

               long keepAliveTime,

               TimeUnit unit,

               BlockingQueue<Runnable> workQueue,

               ThreadFactory threadFactory,

               RejectedExecutionHandler handler)

 

 

 

 

 

 

 

 

这是其中最重要的一个构造方法,这个方法决定了创建出来的线程池的各种属性,下面这张图可以帮助很好的理解线程池和这几个参数;

 

 

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

线程池的执行流程:

 

 

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

handler的拒绝策略:

有四种:

         第一种AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满;

         第二种DisCardPolicy:不执行新任务,也不抛出异常;

         第三种DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行;

         第四种CallerRunsPolicy:直接调用execute来执行当前任务。

5、四种常见的线程池:

CachedThreadPool:可缓存的线程池,该线程池中没有核心线程,非核心线程的数量为Integer.max_value,就是无限大,当有需要时创建线程来执行任务,没有需要时收回线程,适用于耗时少,任务量大的情况。

SecudleThreadPool:周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程和非核心线程,非核心线程的大小也为无限大。适用于执行周期性的任务。

SingleThreadPool:只有一条线程来执行任务,适用于有顺序的任务的应用场景。

FixedThreadPool:定长的线程池,有核心线程,核心线程既为最大的线程数量,没有非核心线程。

转载于:https://www.cnblogs.com/arrows/p/10342523.html

你可能感兴趣的文章
C#事件-事件处理
查看>>
Android——线程通讯类Handler(转)
查看>>
正确使用pthread_create,防止内存泄漏
查看>>
oracle 为 用户 解锁 加锁 (以hr为例)
查看>>
【C++】模板参数推导(template argument deduction)
查看>>
新闻内容翻页
查看>>
VB 读写文件
查看>>
【Linux】time
查看>>
data-compression download
查看>>
多种联结语句
查看>>
ProcesscmdKey KeyDate老是 ProcessKey解决方法
查看>>
git stash和git stash pop
查看>>
[原]Windows批处理命令学习二
查看>>
利用SSLStrip & Ettercap ARP欺骗嗅探密码
查看>>
心血来潮虚拟机安装了centos 6.2,且重新温习了linux下常用命令
查看>>
pku 1611 The Suspects 并查集的应用
查看>>
.Net Framework Windows Debug SOS 扩展常用命令速查[转载]
查看>>
转载 - 不使用任何框架,教你制作网页滑动切换效果
查看>>
【原】NSMutableDictionary与NSMutableArray
查看>>
【转载】如何发送和接收 Windows Phone 的磁贴通知
查看>>