Java多线程

线程
线程,是程序执行的最小单元。
一个标准的线程由线程ID,当前指令指针,寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。
实现线程的三种方式:
(1)自定义一个类,去继承Thread类,重写run方法
(2)自定义一个类,去实现Runnable接口,重写run方法
(3)自定义一个类,实现Callable接口,重写call方法。
Callable规定的方法是call(),而Runnable规定的方法是run().;其次,Callable的任务执行后可返回值,而Runnable的任务是不能返回值的;然后,call()方法可抛出异常,而run()方法是不能抛出异常的;最后,运行Callable任务可拿到一个Future对象。
在这里插入图片描述
并发和并行
并发是两个任务可以在重叠的时间段内启动,运行和完成。
并行是任务在同一时间运行,例如,在多核处理器上。
并发是独立执行过程的组合,而并行是同时执行(可能相关的)计算。
并发是一次处理很多事情,并行是同时做很多事情。
在这里插入图片描述
从微观角度来讲:所有的并发处理都有排队等候,唤醒,执行等这样的步骤,在微观上他们都是序列被处理的,如果是同一时刻到达的请求(或线程)也会根据优先级的不同,而先后进入队列排队等候执行。
从宏观角度来讲:多个几乎同时到达的请求(或线程)在宏观上看就像是同时在被处理。
在这里插入图片描述
并行指两个或两个以上事件(或线程)在同一时刻发生,是真正意义上的不同事件或线程在同一时刻,在不同CPU资源上(多核),同时执行。
多线程
一个任务就是一个线程 ,但实际上,一个应用程序为了同时执行多个任务提高运行效率,一般会涉及到一个线程以上的数量。如果,一个应用程序有一个以上的线程,我们把这种情况就称之为多线程。
本质来说,多线程是为了使得多个线程完成多项任务,以提高系统的效率。目前为止我们使用多线程应用程序的目的是尽可能多地使用计算机处理器资源(本质是为了让效率最大化)。所以,看起来我们仅需要为每个独立的任务分配一个不同的线程,并让处理器确定在任何时间它总会处理其中的某一个任务。但是,这样就会出现一些问题,对小系统来说这样做很好。但是当系统越来越复杂时,线程的数量也会越来越多,操作系统将会花费更多时间去理清线程之间的关系。为了让我们的程序具备可扩展性,我们将不得不对线程进行一些有效的控制。
什么是线程池
线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程(提高线程复用,减少性能开销)。线程池中线程的数量通常完全取决于可用内存数量和应用程序的需求。然而,增加可用线程数量是可能的。线程池中的每个线程都有被分配一个任务,一旦任务已经完成了,线程回到池子中然后等待下一次分配任务。
为什么要用线程池
1.线程池改进了一个应用程序的响应时间。由于线程池中的线程已经准备好且等待被分配任务,应用程序可以直接拿来使用而不用新建一个线程。
2.线程池节省了CLR 为每个短生存周期任务创建一个完整的线程的开销并可以在任务完成后回收资源。
3. 线程池根据当前在系统中运行的进程来优化线程时间片。
4. 线程池允许我们开启多个任务而不用为每个线程设置属性。
5. 线程池允许我们为正在执行的任务的程序参数传递一个包含状态信息的对象引用。
6. 线程池可以用来解决处理一个特定请求最大线程数量限制问题。
本质上来讲,我们使用线程池主要就是为了减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务;节约应用内存(线程开的越多,消耗的内存也就越大,最后死机)。
线程池的作用
线程池作用就是限制系统中执行线程的数量。根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;减少了系统资源的浪费,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。
在Java中,线程池的代码起源之Executor(翻译过来就是执行者)注意:这个类是一个接口。
public interface Executor{
void execute(Runnable command);
}
Executor并不是一个线程池,Executor只是一个执行线程的工具。Executor有一个ExecutorServiece接口。ExecutorService源码里面有各种API(比如说执行 excute ( xxx ),比如关闭 isShutdown ( ))帮助我们去使用。ExecutorService接口的默认实现类为ThreadPoolExecutor(翻译过来就是线程池执行者)。
ThreadPoolExecutor的构造方法有四种:
在这里插入图片描述
(1) int corePoolSize (core:核心的) = > 该线程池中核心线程数最大值
核心线程:线程池新建线程的时候,如果当前线程总数小于 corePoolSize ,则新建的是核心线程;如果超过corePoolSize,则新建的是非核心线程。
核心线程默认情况下会一直存活在线程池中,即使这个核心线程啥也不干(闲置状态)。
如果指定ThreadPoolExecutor的 allowCoreThreadTimeOut 这个属性为true,那么核心线程如果不干活(闲置状态)的话,超过一定时间( keepAliveTime),就会被销毁掉。
(2) int maximumPoolSize = > 该线程池中线程总数的最大值
线程总数计算公式 = 核心线程数 + 非核心线程数。
(3) long keepAliveTime = > 该线程池中非核心线程闲置超时时长
一个非核心线程,如果不干活(闲置状态)的时长,超过这个参数所设定的时长,就会被销毁掉。但是,如果设置了 allowCoreThreadTimeOut = true,则会作用于核心线程。
(4) TimeUnit unit = > (时间单位)
首先,TimeUnit是一个枚举类型,翻译过来就是时间单位,我们最常用的时间单位包括:
MILLISECONDS : 1毫秒 、SECONDS : 秒、MINUTES : 分、HOURS : 小时、DAYS : 天
(5) BlockingQueue<Runnable> workQueue = >( Blocking:阻塞的,queue:队列)
该线程池中的任务队列:维护着等待执行的Runnable对象。当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务。
向线程池中添加任务
在这里插入图片描述
首先我们初始化一个线程池后,即可调用execute这个方法,里面传入Runnable即可向线程池添加任务。
线程池如何处理批量任务
(1)如果线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务;
(2)如果线程数量达到了corePools,则将任务移入队列等待;
(3)如果队列已满,新建线程(非核心线程)执行任务;
(4)如果队列已满,总线程数又达到了maximumPoolSize,就会由RejectedExecutionHandler抛出异常。
Java为我们提供了四种线程池
A:newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
B:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
C:newScheduledThreadPool 创建一个定长任务线程池,支持定时及周期性任务执行。
D:newSingleThreadExecutor 创建一个单线程的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值