关于线程、进程、并行、并发、线程池的概念介绍
一、什么是线程和进程
线程,程序执行流的最小执行单位,是行程中的实际运作单位,经常容易和进程这个概念混淆。
那么,线程和进程究竟有什么区别呢?
首先,进程是一个动态的过程,是一个活动的实体。简单来说,一个应用程序的运行就可以被看做是一个进程,而线程,是运行中的实际的任务执行者。可以说,进程中包含了多个可以同时运行的线程。
二、线程的生命周期
1. 是用new Thread()的方法新建一个线程,在线程创建完成之后,线程就进入了就绪(Runnable)状态,此时创建出来的线程进入抢占CPU资源的状态,当线程抢到了CPU的执行权之后,线程就进入了运行状态(Running),当该线程的任务执行完成之后或者是非常态的调用的stop()方法之后,线程就进入了死亡状态。
2. 而我们在图解中可以看出,线程还具有一个阻塞的过程,这是怎么回事呢?当面对以下几种情况的时候,容易造成线程阻塞:
(1)第一种,当线程主动调用了sleep()方法时,线程会进入则阻塞状态,除此之外,当线程中主动调用了阻塞时的IO方法时,这个方法有一个返回参数,当参数返回之前,线程也会进入阻塞状态;
(2)还有一种情况,当线程进入正在等待某个通知时,会进入阻塞状态。
那么,为什么会有阻塞状态出现呢?我们都知道,CPU的资源是十分宝贵的,所以,当线程正在进行某种不确定时长的任务时,Java就会收回CPU的执行权,从而合理应用CPU的资源。我们根据图可以看出,线程在阻塞过程结束之后,会重新进入就绪状态,重新抢夺CPU资源。这时候,我们可能会产生一个疑问,如何跳出阻塞过程呢?由以上几种可能造成线程阻塞的情况来看,都是存在一个时间限制的,当sleep()方法的睡眠时长过去后,线程就自动跳出了阻塞状态,第二种则是在返回了一个参数之后,在获取到了等待的通知时,就自动跳出了线程的阻塞过程。
三、单线程和多线程
(1)单线程,就是只有一条线程在执行任务;
(2)多线程,创建多条线程同时执行任务;
四、并行和并发
(1)并行:则是真正意义上的同时进行多种事情,即在一个时间段同时进行多种任务,这种情况只可以在多核CPU的基础下完成;
(2)并发:从宏观方面来说,并发就是同时进行多种单位时间,实际上,这几种单位时间,并不是同时进行的,而是交替进行的,而由于CPU的运算速度非常的快,会造成我们的一种错觉,就是在同一时间内进行了多种事情,实际上在某一时间段只进行了一件事情;
五、线程的安全问题
我们可以想象一下,如果多个线程同时执行一个任务,也就意味着他们共享同一种资源,由于线程CPU的资源不一定可以被哪个线程抢占到,假如,第一条线程先抢占到CPU资源,他刚刚进行了第一次操作,而此时第二条线程抢占到了CPU的资源,那么,共享资源还来不及发生变化,就同时有两条数据使用了同一条资源,具体请参考多线程买票问题。这个问题主要的矛盾在于,CPU的使用权抢占和资源的共享发生了冲突,解决时,我们只需要让一条线程占有CPU资源时,阻止第二条线程同时抢占CPU的执行权,在代码中表现在,我们只需要在方法中使用同步代码块即可。
六、线程池
在一个应用程序中,我们需要多次使用线程,也就意味着,我们需要多次创建并销毁线程。而创建并销毁线程的过程势必会消耗内存。而在Java中,内存资源是及其宝贵的,所以,就提出了线程池的概念。
线程池:Java中开辟出了一种管理线程的概念,这个概念叫做线程池,从概念以及应用场景中,我们可以看出,线程池的好处,就是可以方便的管理线程,也可以减少内存的消耗。
七、运用
Java中已经提供了创建线程池的一个类:Executor,而我们创建线程时时,一般使用它的子类:ThreadPoolExecutor。构造函数如下:
public ThreadPoolExecutor(
int corePoolSize, //线程池中核心线程的数量;
int maximumPoolSize, //线程池中可容纳的最大线程数;
long keepAliveTime, //线程池中除了核心线程外其他线程的的最长保留时间,也称作存活时间;
TimeUnit unit, //计算线程存活时间的单位;
BlockingQueue<Runnable> workQueue, //等待队列,待执行任务存储在等待队列中等待执行,先进先出;
ThreadFactory threadFactory, //线程创建工厂;
RejectedExecutionHandler handler //拒绝策略,在任务满了之后,可以拒绝执行某些任务;
)
各个参数具体理解如图:
八、handler拒绝策略
第一种AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满;
第二种DisCardPolicy:不执行新任务,也不抛出异常;
第三种DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行;
第四种CallerRunsPolicy:直接调用execute来执行当前任务;
九、常见的线程池
CachedThreadPool:可缓存的线程池,该线程池中没有核心线程,非核心线程的数量为Integer.max_value,就是无限大,当有需要时创建线程来执行任务,没有需要时回收线程,适用于耗时少,任务量大的情况;
SecudleThreadPool:周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程,非核心线程的大小也为无限大。适用于执行周期性的任务;
SingleThreadPool:只有一条线程来执行任务,适用于有顺序的任务的应用场景;
FixedThreadPool:定长的线程池,有核心线程,核心线程的即为最大的线程数量,没有非核心线程。