## 为什么要多线程
为什么要多线程啊?操作系统有多进程管理,那多线程感觉没有必要。
所谓线程,是程序代码的执行,一个进程至少得一个线程,不然进程如何执行。
比如说word,如果这个进程没有多线程,如果有个定时保存文档的功能,当自动保存的时候,还能继续输入文字吗?当然是不能的。因为单线程只能干一件事,无法并发和并性,直接导致用户体验不好。CPU的快速运算能力还有多核就被浪费了。
那么为什么不能用多进程来处理呢?一个进程来接受用户输入的文字,另一个进程来自动保存。但是我们需要知道,进程之间是相互隔离的,他们要共享数据,是非常麻烦的。比如说要共享被编辑的文件内容并不容易。
可以说,进程是拥有资源的基本单位,线程是CPU调度的基本单位。
比如有两个进程,word和QQ。
Word进程打开文件,这是它的资源,QQ打开了Socket,这也是它的资源。
假设word有两个线程,T1负责用户文字输入,T2负责自动保存
QQ也有两个线程,T3负责从Socket读取数据,T4负责对音乐数据进行解码。
操作系统在做调度的时候,基本单位不是word,QQ,而是T1,T2,T3这些线程。
![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180824095135.png?picgo)
每个线程执行的都是进程代码中的某个片段。
我们知道Java中总是讨论多线程编程,但是从来没有提过Java多进程,为什么呢?
因为java是一运行在JVM中,对于操作系统来说,JVM就是java.exe运行起来,也就是个进程。
所以说JVM这个进程其实就是他们的容器。
另外Python等动态语言也有虚拟机,也可以进行多线程编程了。
所以说虚拟机是个好东西,程序员不需要再去操作内存,可以屏蔽操作系统的差异,让写的程序可以任意在支持该语言虚拟机上的操作系统上运行。
为什么在Java中创建的Thread对象需要调用start方法才能启动线程,而不是直接调用run呢?如果直接调用run的话,只是用当前线程去执行一个普通的函数,根本没有新线程创建出来。
所以,想创建一个新的线程出来,必须有准备工作,比如设置好线程的上下文,比如栈(相当于函数调用)、线程状态,比如线程的PC(Program Counter)等,线程才可以被调度,一旦被调度,就会执行run()方法了。
## 线程池
既然线程是属于进程的,那么创建一个线程应该很轻松啊,为什么要有线程池这个东西呢?
因为线程虽然轻量,但是对与互联网应用,每个用户的请求都创建一个线程,那会非常多。而且众多的线程去竞争CPU,不断的切换,也会让CPU调度不堪重负,很多线程不得不等待。
所以最好的方法用少量的线程,并且让这些线程尽可能充分利用。也就是说只创建一定数量的线程,让这些线程来处理所有的任务,任务执行完了以后,线程不结束,而是回到线程池里面,等待接受下一个任务。
![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180824100039.png?picgo)
这些线程可以预先创建,任务来就不用临时再创建了,可以立刻开始服务。
但是线程是程序代码的执行,是个动态的东西,怎么可以预先创建呢?
这是因为线程是有状态的,当线程池的线程刚刚创建的时候,会让他们进入阻塞状态:等待任务的到来。如果任务来了,就唤醒其中的线程,让它拿到任务去执行。
那如何让线程进入阻塞状态呢?
就是一个线程调用take()方法取数据的时候,如果这个Queue中没有数据,该线程就会阻塞,同样一个线程调用它的put方法放数据的时候,如果Queue满了,也会阻塞。
![](http://p8a6vmhkm.bkt.clouddn.com/picgo20180824100323.png?picgo)
所以说线程池的每个线程的run()方法中,要设置一个循环,每次都尝试从BlockingQueue获取任务,如果Queue是空的,就阻塞等待,如果有任务来了,就会通知到线程吃的某个线程去处理,橱窗完了,试图从BlockingQueue中获取任务,依次循环下去。
~~~
线程池中的Worker线程:
public class WorkerThread extends Thread {
private BlockingQueue<Task> taskQueue = null;
private boolean isStopped = false;
//持有一个BlockingQueue的实例
public WorkerThread(BlockingQueue<Task> queue){
taskQueue = queue;
}
public void run(){
while(!isStopped()){
try{
Task task = taskQueue.take();
task.execute();
} catch(Exception e){
//log or otherwise report exception,
//but keep pool thread alive.
}
}
}
......略......
}
~~~
这套代码已经被吸收到JDK,作为java.util.concurrent包的一部分。