任何一个伟大的目标,都有一个微不足道的开始。

关于线程、进程、并行、并发、线程池的概念介绍

一、什么是线程和进程

线程,程序执行流的最小执行单位,是行程中的实际运作单位,经常容易和进程这个概念混淆。

那么,线程和进程究竟有什么区别呢?

首先,进程是一个动态的过程,是一个活动的实体。简单来说,一个应用程序的运行就可以被看做是一个进程,而线程,是运行中的实际的任务执行者。可以说,进程中包含了多个可以同时运行的线程。

 

二、线程的生命周期

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:定长的线程池,有核心线程,核心线程的即为最大的线程数量,没有非核心线程。

posted @ 2021-09-10 20:36  BigPigKing  阅读(150)  评论(0编辑  收藏  举报