线程创建的基本知识
(1)创建线程
创建线程有两种方式:继承Thread或实现Runnable。
Thread实现了Runnable接口,提供了一个空的run()方法,所以不论是继承Thread还是实现Runnable,都要有自己的run()方法。
一个线程创建后就存在,调用start()方法就开始运行,调用wait进入等待或调用sleep进入休眠期,顺利运行完毕或休眠被中断或运行过程中出现异常而退出销毁。
(2)线程的生命周期
当线程被创建之后,不是一启动就进入执行状态,也不是一直处于执行状态,
在其生命周期中,要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)五种状态。
线程在创建之后,不可能一直霸占着CPU独立运行,需要在多个线程之间切换,所以大部分时间处于运行、阻塞之间切换。
(3)Java线程间的状态转换
新建(new):新创建了一个线程对象。
可运行(runnable):线程对象创建后,调用了该对象的start()方法。该状态的线程位于就绪线程池中,不能立刻进入运行状态,要等待JVM里线程调度器的调度,获取cpu 的使用权。注意,只能对处于新建状态的线程调用start()方法,否则会引发异常。
运行(running):如果处于就绪状态的线程获得了CPU,就开始执行run方法,处于了运行状态。
当分配的时间用完后,又进入了就绪状态,等待下次分配到CPU在进入运行状态。
阻塞(block):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。
死亡(dead):线程run()方法执行结束,线程正常结束;或者线程抛出一个未捕获的Exception或Error,因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。注意,如果通过主线程启动其他线程,当主线程结束时,其他子线程不受影响,并不会随之结束。
使用线程池的意义
(1)线程复用
使用线程池技术可以实现工作线程的复用,即一个工作线程创建和销毁的生命周期期间内可以执行处理多个任务,从而总体上降低线程创建和销毁的频率和时间,提升了系统性能。
(2)并发控制,平滑高峰请求
线程池中的任务队列起到了一个缓冲的作用,服务器资源有限,超过服务器性能的过高并发设置会成为系统的负担,造成CPU大量耗费于上下文切换,可能会产生内存溢出等后果。
通过线程池技术可以控制系统最大并发数和最大处理任务量,从而很好的实现流量控制,保证系统不至于崩溃。
线程池的应用和设计
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。
服务器启动一个线程完成一项任务所需时间可以分为三个部分:
创建线程时间T1,在线程中执行任务的时间T2,销毁线程时间为T3。
如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。
线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务
器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1.T3的开销了。
线程池不仅调整T1,T3产生的时间段,而且还可以显著减少创建线程的数目。
一个线程池包括以下四个基本组成部分:
1、线程池管理器(ThreadPool):用于创建并管理线程池,包括创建线程池,销毁线程池,添加新任务;
2、工作线程(PoolWorker):我们把用来执行用户任务的线程称为工作线程,工作线程就是不断从队列中获取任务对象并执行对象上的业务方法。
线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
自定义线程池实现和测试
了解了线程池的设计,就可以实现自己的线程池。