同一个进程中的多个线程之间可以并发执行,一个程序至少有一个进程,一个进程至少有一个线程
一个应用程序就是一个进程,进程就是在某种程度上相互隔离的、独立运行的程序。而线程是一个进程内部的多个运行单位。在Java中的并发机制可以这样理解,我们在使用计算机看视频的同时可以用它来打印文件,这些活动是可以同时进行的,而我们可以把线程理解为在程序中并发完成的每一件事——即线程是程序内部一个独立的运行单位。
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,但是它可与同属一个进程的其他线程共享进程所拥有的全部资源。
在我们以往编写的程序中,都是在一个任务完成后再进行下一个项目的开发,这样下一个任务的开始必须等前一个任务的完成。而我们通过Java语言的并发机制,可以在程序中执行多个线程,每一个线程完成一个功能,所有线程并发执行,这种机制就叫做多线程。
计算机的核心是CPU,承担全部运算任务。就像一个工厂,无时无刻在运行,设定这个工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。一个车间里,可以有很多工人。他们协同完成一个任务。线程就好比车间里的工人。一个进程可以包括多个线程。车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。可是,每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。
自己手动为服务端创建线程,当服务端逻辑运行完了以后,线程就会被销毁。
这种做法会有大量的线程创建与销毁的工作,另外,线程在创建了以后,短时间内可以创建多少线程,线程之间的调度竞争等等,其实都会带来很大的问题。而大多数情况下,所有的多线程程序其实都会面临相同的问题。所以,干脆,JDK引入了一个统一的模型来解决这些共同的问题,这就是线程池。
线程池的思想是这样,创建固定数目的线程,当有任务需要在单独的线程中独立运行的时候,就从这些固定数目的线程中选择一个,在这个线程上执行。当任务执行完了,就会把线程还回去。这些线程就好像放在一个池子中的水一样,用的时候捞上来用一下,用完了就还回去。所以,给它起了一个很形象的名字,叫线程池。资源的池化是非常常见的一种使用空间缓存缓解系统瞬时压力的解决方案。
AbortPolicy
(抛出RejectedExecutionException异常)、CallerRunsPolicy
(由提交任务的线程执行任务)、DiscardPolicy
(直接丢弃任务)等。RejectedExecutionException
异常。ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
timeUnit,
workQueue,
handler); // 默认是AbortPolicy
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
timeUnit,
workQueue,
new ThreadPoolExecutor.CallerRunsPolicy());
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
timeUnit,
workQueue,
new ThreadPoolExecutor.DiscardPolicy());
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
timeUnit,
workQueue,
new ThreadPoolExecutor.DiscardOldestPolicy());
如果任务的重要性较高,可使用CallerRunsPolicy
,以确保任务一定会被执行,尽管这可能会影响整体性能。如果对任务的执行没有特殊要求,可以使用默认的AbortPolicy。
四、五种状态
线程有5种状态:新建状态,就绪状态,运行状态,阻塞状态,死亡状态。
线程池也有5种状态:Running,SHUTDOWN,STOP,TIDYING,TERMINATED。
线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态!
在ctl的初始化代码中(如下),就将它初始化为RUNNING状态,并且"任务数量"初始化为0。
privatefinalAtomicInteger ctl =newAtomicInteger(ctlOf(RUNNING, 0));
线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。
线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。
当所有的任务已终止,ctl记录的"任务数量"为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。
线程池彻底终止,就变成TERMINATED状态。
线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。