We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[TOC]
这篇文章了解一下 多线程 相关的知识。
操作系统的设计者 巧妙地利用了时间片轮转的方式, CPU给每个任务都服务一定的时间,然后把当前任务的状态保存下来,在加载下一任务的状态后,继续服务下一任务。任务的状态保存及再加载, 这段过程就叫做上下文切换。时间片轮转的方式使多个任务在同一颗CPU上执行变成了可能。
由于频繁的创建/销毁线程,对内存的消耗很严重,这是就需要线程池。其优点在于:
线程池属于对象池.所有对象池都具有一个非常重要的共性,就是为了最大程度复用对象.那么线程池的最重要的特征也就是最大程度利用线程。
首先,创建线程本身需要额外(相对于执行任务而必须的资源)的开销. 作业系统在每创建一个线程时,至少需要创建以下资源: (1) 线程内核对象:用于对线程上下文的管理. (2) 用户模式执行栈. (3) 内核模式执行栈. 这些资源被线程占有后作业系统和用户都无法使用. 相反的过程,销毁线程需要回收资源,也需要一定开销. 其次,过多的线程将导致过度的切换.线程切换带来的性能更是不可估量.系统完成线程切换要经过以下过程: (1) 从用户模式切换到内核模式. (2) 将CPU寄存器的值保存到当前线程的内核对象中. (3)打开一个自旋锁,根据调度策略决定下一个要执行的线程.释放自旋锁,如果要执行的线程不是同一进程中的线程,还需要切换虚拟内存等进程环境. (4) 将要执行的线程的内核对象的值写到CPU寄存器中. (5) 切换到用户模式执行新线程的执行逻辑. 所以线程池的目的就是为了减少创建和切换线程的额外开销,利用已经的线程多次循环执行多个任务从而提高系统的处理能力。
ExecutorService 是最初的线程池接口,而 ThreadPoolExecutor 是具体实现。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); // 核心线程,无任务运行也存在。如果 allowCoreThreadTimeOut 为 true 也会存在超时策略。 this.corePoolSize = corePoolSize; // 最大线程数:当任务超过核心线程并且 workQueue 已经满时创建,用完销毁。 this.maximumPoolSize = maximumPoolSize; // 线程池任务队列 this.workQueue = workQueue; // 非核心线程的超时时间。超时则被回收。 this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
线程池的分配遵循这样的规则:
针对不同情况,一般需要不同的线程池策略,一般情况下有四种:
合理设置线程数目,关键点是:1. 尽量减少线程切换和管理的开支;2. 最大化利用CPU;
对于1,要求线程数尽量少,这样可以减少线程切换和管理的开支; 对于2,要求尽量多的线程,以保证CPU资源最大化的利用;
对于1,要求线程数尽量少,这样可以减少线程切换和管理的开支;
对于2,要求尽量多的线程,以保证CPU资源最大化的利用;
所以 对于任务耗时短的情况,要求线程尽量少,如果线程太多,有可能出现线程切换和管理的时间,大于任务执行的时间,那效率就低了;
对于耗时长的任务,要分是CPU任务,还是IO等类型的任务。如果是CPU类型的任务,线程数不宜太多;但是如果是IO类型的任务,线程多一些更好,可以更充分利用CPU。
高并发,低耗时的情况:建议少线程,只要满足并发即可,因为上下文切换本来就多,并且高并发就意味着CPU是处于繁忙状态的, 增加更多地线程也不会让线程得到执行时间片,反而会增加线程切换的开销;例如并发100,线程池可能设置为10就可以;
低并发,高耗时的情况:建议多线程,保证有空闲线程,接受新的任务;例如并发10,线程池可能就要设置为20;
The text was updated successfully, but these errors were encountered:
No branches or pull requests
Android Multi Thread
[TOC]
这篇文章了解一下 多线程 相关的知识。
线程
操作系统的设计者 巧妙地利用了时间片轮转的方式, CPU给每个任务都服务一定的时间,然后把当前任务的状态保存下来,在加载下一任务的状态后,继续服务下一任务。任务的状态保存及再加载, 这段过程就叫做上下文切换。时间片轮转的方式使多个任务在同一颗CPU上执行变成了可能。
线程上下文切换的原因
线程池
由于频繁的创建/销毁线程,对内存的消耗很严重,这是就需要线程池。其优点在于:
线程池属于对象池.所有对象池都具有一个非常重要的共性,就是为了最大程度复用对象.那么线程池的最重要的特征也就是最大程度利用线程。
首先,创建线程本身需要额外(相对于执行任务而必须的资源)的开销.
作业系统在每创建一个线程时,至少需要创建以下资源:
(1) 线程内核对象:用于对线程上下文的管理.
(2) 用户模式执行栈.
(3) 内核模式执行栈.
这些资源被线程占有后作业系统和用户都无法使用.
相反的过程,销毁线程需要回收资源,也需要一定开销.
其次,过多的线程将导致过度的切换.线程切换带来的性能更是不可估量.系统完成线程切换要经过以下过程:
(1) 从用户模式切换到内核模式.
(2) 将CPU寄存器的值保存到当前线程的内核对象中.
(3)打开一个自旋锁,根据调度策略决定下一个要执行的线程.释放自旋锁,如果要执行的线程不是同一进程中的线程,还需要切换虚拟内存等进程环境.
(4) 将要执行的线程的内核对象的值写到CPU寄存器中.
(5) 切换到用户模式执行新线程的执行逻辑.
所以线程池的目的就是为了减少创建和切换线程的额外开销,利用已经的线程多次循环执行多个任务从而提高系统的处理能力。
ThreadPoolExecutor
ExecutorService 是最初的线程池接口,而 ThreadPoolExecutor 是具体实现。
线程池的分配遵循这样的规则:
线程池的分类
针对不同情况,一般需要不同的线程池策略,一般情况下有四种:
核心线程的数量
合理设置线程数目,关键点是:1. 尽量减少线程切换和管理的开支;2. 最大化利用CPU;
所以 对于任务耗时短的情况,要求线程尽量少,如果线程太多,有可能出现线程切换和管理的时间,大于任务执行的时间,那效率就低了;
对于耗时长的任务,要分是CPU任务,还是IO等类型的任务。如果是CPU类型的任务,线程数不宜太多;但是如果是IO类型的任务,线程多一些更好,可以更充分利用CPU。
高并发,低耗时的情况:建议少线程,只要满足并发即可,因为上下文切换本来就多,并且高并发就意味着CPU是处于繁忙状态的, 增加更多地线程也不会让线程得到执行时间片,反而会增加线程切换的开销;例如并发100,线程池可能设置为10就可以;
低并发,高耗时的情况:建议多线程,保证有空闲线程,接受新的任务;例如并发10,线程池可能就要设置为20;
The text was updated successfully, but these errors were encountered: