一、线程池
我们现在去坐地铁,如果没一卡通,需要购买一张临时票。
在很早以前,票是可以被带走的,这样票基本都被浪费了,并且每天需要的开销也很大。
所以后来有了改进,现在临时票在出地铁站时会被回收,回收之后又能被其他人使用,这样资源就被重复利用起来了。
线程就相当于临时卡,我们之前是如何使用线程的?
直接调用Thread的start方法启动。
这样做有什么坏处?
如果我们有一个项目需要多次使用到线程,就得不停地创建和销毁,这样就太浪费资源了。
所以就和临时票一样,要把线程重复利用起来,先搞一个容器,装一些线程,用的时候拿出来,不用的时候放回去,这样资源不会被销毁,就节省了很多资源。
这样做有什么好处?
线程池的最顶层的接口是:Executor,ExecutorService是Executor的子接口。
Executors是线程池对应的工具类(Java中很多工具类都是后面加一个s)。
①创建一个数量为3的线程池。
newFixedThreadPool()方法,不要看这个方法名特长,其实拆分开来理解蛮简单的。
new:新的,可以理解成创建;Fixed:固定的;Thread:线程;Pool:池子。
创建一个固定的线程池,就是该方法的作用,其中参数为3表示该线程池里有3个线程。
②创建Runnable的子类对象。
和创建线程一样,也是需要这个Runnable;
重写run方法,打印当前线程的名字。
③execute方法:
Runnable作为参数,开启线程池中的一个线程。
for循环5次,表示开启了五个线程。
④shutdown方法:
这个很好理解,关闭的意思,也就是说关闭线程池。
不过线程池打开了一般都是不会关闭的。
这也好理解:很多大公司都不会关服务器,你什么时候见过百度不能访问了的?一样的道理。
好,现在代码写完了,我们运行看看,因为线程启动时,Java虚拟机会调用run方法,所以控制台会打印②中run方法里的内容:也就是当前线程的名字。
运行结果如下:
从中我们可以知道:
线程池的名字就叫pool-1,线程池里面有3个线程,分别为thread-1,thread-2,thread-3。
那其中为何thread打印了三次?
因为我们for循环了5次,所以有5个线程。
但是线程池里面一共就这3个线程,就让它们循环使用。
说完了线程池,再补充一个知识点:线程的六种状态。
因为属于理论知识点,很难用代码来演示,所以需要理解后再记忆,毕竟面试啊、计算机考试啊可能会用到。
画图如下:
我们再来一个个详细讲解:
1.NEW(新建状态)
线程刚被创建,但是并未启动,还没调用start方法,new本身就可以理解成创建的意思,new对象new对象,不就是在创建对象嘛。
2.Runnable(可运行):这也是核心状态。
线程可以在Java虚拟机中运行的状态,可能正在运行自己代码,也可能没有。
如何理解这句话?
Thread调用了start方法,线程启动,但它是立刻就运行了么?
是不一定的,因为这取决于cpu,电脑的逻辑处理器可能正在处理其他的线程,并没有处理Java虚拟机里的线程。
但是cpu处理速度太快太快了,所以我们感觉它好像立刻就运行了,但实际上并不一定。
3.Blocked(锁阻塞)
当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入锁阻塞状态;当该线程持有锁时,该线程将变成Runnable状态。
什么意思呢?
我们知道有了锁之后,一次只能运行一个线程,另外的线程就只能在外面待着,此时它们便处于锁阻塞状态。
4.Waiting(无限等待)
一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态,进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
5.TimedWaiting(计时等待)
还记得线程的sleep方法吗?
让一个线程睡多少毫秒,这种情况下的线程就处于计时等待状态,它和无限等待状态很像,只不过该状态到了时间就自动醒,无限等待得要别的线程唤醒。
6.Terminated(被终止)
这个很好理解,要么运行结束了,要么遇到异常被强行终止了。