线程可认为是操作系统可调度的最小的程序执行序列,一般作为进程的组成部分,同一进程中多个线程可共享该进程的资源(如内存等)。jvm线程跟内核轻量级进程有一对一的映射关系,所以jvm中的线程是很宝贵的。
一般在工程上多线程的实现是基于线程池的。因为相比自己创建线程,多线程具有以下优点
线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用。可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃。看阿里巴巴开发手册并发编程这块有一条:线程池不允许使用executors去创建,而是通过threadpoolexecutor的方式。
当一个任务通过execute(runnable)方法欲添加到线程池时:
如果此时线程池中的数量小于corepoolsize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。如果此时线程池中的数量等于 corepoolsize,但是缓冲队列 workqueue未满,那么任务被放入缓冲队列。如果此时线程池中的数量大于corepoolsize,缓冲队列workqueue满,并且线程池中的数量小于maximumpoolsize,建新的线程来处理被添加的任务。那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corepoolsize、任务队列workqueue、最大线程maximumpoolsize,如果三者都满了,使用handler处理被拒绝的任务。当线程池中的线程数量大于 corepoolsize时,如果某线程空闲时间超过keepalivetime,线程将被终止。这样,线程池可以动态地调整池中的线程数。java中的blockingqueue主要有两种实现,分别是arrayblockingqueue 和 linkedblockingqueue。
arrayblockingqueue是一个用数组实现的有界阻塞队列,必须设置容量。
linkedblockingqueue是一个用链表实现的有界阻塞队列,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为integer.max_value。
这里的问题就出在:不设置的话,将是一个无边界的阻塞队列,最大长度为integer.max_value写秋天的四字词语。也就是说,如果我们不设置linkedblockingqueue的容量的话,其默认容量将会是integer.max_value。
而newfixedthreadpool中创建linkedblockingqueue时,并未指定容量。此时,linkedblockingqueue就是一个无边界队列,对于一个无边界队列来说,是可以不断十月一祝福语地向队列中加入任务的,这种情况下就有可能因为任务过多而导致内存溢出问题。
结合上述流程图,核心线程数=0,最大线程无限大,由于synchronousqueue是一个缓存值为1的阻塞队列。当有大量任务请求时,线程池会创建大量线程,造成oom。
/** * @param corepoolsize 核心线程数 * @param maximumpoolsize 最大线程数 * @param keepalivetime 线程所允许的空闲时间 * @param unit 线程所允许的空闲时间的单位 * @param workqueue 线程池所使用的缓冲队列 * @param handler 线程池对拒绝任务的处理策略 */threadpoolexecutor(int corepoolsize, int maximumpoolsize, long keepalivetime, timeunit unit, blockingqueue<runnable> workqueue, rejecte卢沟桥事变是哪一年dexecutionhandler handler)
rejectedexecutionhandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是abortpolicy,表示无法处理新任务时抛出异常。。以下是jdk1.5提供的四种策略。
abortpolicy:直接抛出异常callerrunspolicy:只用调用者所在线程来运行任务。discardoldestpolicy:丢弃队列里最近的一个任务,并执行当前任务。discardpolicy:不处理,丢弃掉。当然也可以根据应用场景需要来实现rejectedexecutionhandler接口自定义策略。如记录日志或持久化不能处理的任务。避免使用executors创建线程池,主要是避免使用其中的默认实现,那么我们可以自己直接调用threadpoolexecutor的构造函数来自己创建线程池。在创建的同时,给blockqueue指定容量就可以了。
threadpoolexecutor executorrvice = new threadpoolexecutor(8, 16, 60, timeunit.conds, ne北京条约w linkedblockingdeque<>(10));
我们可以使用execute提交的任务,但是execute方法没有返回值,所以无法判断任务是否被线程池执行成功。通过以下代码可知execute方法输入的任务是一个runnable类的实例。
threadpoolexecutor threadpoolexecutor = new threadpoolexecutor(10, 20, 5, timeunit.conds, new linkedblockingdeque<>(60)); threadpoolexecutor.execute(new runnable() { @override public void run() { system.out购买联想电脑.println("线程池无返回结果"); } });
我们也可以使用submit 方法来提交任务,它会返回一个future,那么我们可以通过这个future来判断任务是否执行成功,通过future的get方法来获取返回值,get方法会阻塞住直到任务完成,而使用get(long timeout, timeunit unit)方法则会阻塞一段时间后立即返回,这时有可能任务没有执行完。
threadpoolexecutor threadpoolexecutor = new threadpoolexecutor(10, 20, 5, timeunit.conds, new linkedblockingdeque<>(60)); future<string> future = threadpoolexecutor.submit(new callable<string>() { @override public string call() throws exception { return "ok"; } }); system.out.println("线程池返回结果:" + future.get());
shutdown关闭线程池
方法定义:public void shutdown()
(1)线程池的状态变成shutdown状态,此时不能再往线程池中添加新的任务,否则会抛出
rejectedexecutionexception异常。
(2)线程池不会立刻退出,直到添加到线程池中的任务都已经处理完成,才会退出。
注意这个函数不会等待提交的任务执行完成,要想等待全部任务完成,可以调用:
public boolean awaittermination(longtimeout, timeunit unit)
shutdownnow关闭线程池并中断任务
方法定义:public list shutdownnow()
(1)线程池的状态立刻变成stop状态,此时不能再往线程池中添加新的任务。
(2)终止等待执行的线程,并返回它们的列表;
(3)试图停止所有正在执行的线程,试图终止的方法是调用thread.interrupt(),但是大家知道,如果线程中没有sleep 、wait、condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。所以,shutdownnow()并不代表线程池就一定立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。
cpu密集型任务
该任务需要大量的运算,并且没有阻塞,cpu一直全速运行,cpu密集任务只有在真正的多核cpu上才可能通过多线程加速 cpu密集型任务配置尽可能少的线程数量:
cpu核数+1个线程的线程池。
例如: cpu 16核,内存32g。线程数=16
io密集型任务
io密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如cpu核数*2
某大厂设置策略:io密集型时,大部分线程都阻塞,故需要多配置线程数:
cpu核数/(1-阻塞系数)
例如: cpu 16核, 阻塞系数 0.9 ————->16/(1-0.9) = 160 个线程数。
此时非阻塞线程=16
本文发布于:2023-04-05 15:26:30,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/d70b26463ca1b94fb0fbfe1c225b925a.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:电脑多线程有什么用(解析电脑多线程用处和应用).doc
本文 PDF 下载地址:电脑多线程有什么用(解析电脑多线程用处和应用).pdf
留言与评论(共有 0 条评论) |