目录
前言:
1.什么是线程池
2.标准库中的线程池
3.实现线程池
结束语:
在上一节中小编带着大家了解了一下Java标准库中的定时器的使用方式并给大家实现了一下,那么这节中小编将分享一下多线程中的线程池。给大家讲解一下什么是“池”,为什么要使用线程池。
之前我们也有讲过“池”这个概念,我们讲过字符串常量池,数据连接池...
线程池就是提前把线程准备好,创建线程不是直接从系统中申请而是从池子中拿,当线程不用了的时候也是还给池子。它存在的目的就是为了提高效率,它最大的好处就是减少每次启动、销毁线程的损耗。线程的创建虽然比进程轻量,但是在频繁的创建的情况下,开销也是不可忽略的。
那么为什么从池子里拿比创建线程要更高效呢?
在上述过程中提到了用户态、内核态这两个概念,那么下来给大家解释一下什么是用户态,什么是内核态。
一个操作系统 = 内核 + 配套的应用程序
比如我们执行:println("hello")这样的一个操作。
首先应用程序调用系统内核,告诉内核我要进行打印一个字符串的操作,内核再通过驱动程序,操作显示器来完成上述的请求。应用程序有很多,但是内核只有一个,内核要给这么多程序提供服务有的时候服务就不会那么及时。
举一个例子:比如银行里的工作人员和来银行办理事务的人,假设工作人员只有一个人,客户有很多,此时工作人员就相当于内核,客户就是用户。工作人员待的柜台就是用户态,银行的大厅就是用户态。如下图所示:
滑稽A此时来到柜台给工作人员说:我想办张银行卡。
此时就需要复印一些文件,这时我们有两种解决办法:
那么此时如果A滑稽自己去复印的话速度就会很快,立即复印,立即回来。但是如果是工作人员去复印的话可能就会很慢,因为柜台只有它一个人,他可能还需要给其他人提供服务。所以这也就例比于我们计算机中的用户态操作和内核态操作了,A滑稽自己复印就是用户态操作,而工作人员复印就是内核态操作。
结论:纯用户态操作,时间是可控的。涉及到内核态操作,时间就不太可控了。
Java标准库中提供了线程的线程池。
ExecutorService pool = Executors.newFixedThreadPool(10);
在这里我们可以看到和我们之前创建一个对象不太一样。
注意:这里我们使用的是工厂模式!!!
创建对象的时候不再直接new,而是使用一些其他方法(通常是静态方法)协助我们把对象创建出来。
在Executors.newFixedThreadPoll()是不会设定固定值的,这里是按需创建,用完之后也不会立即销毁,留着以备后用。
下面我们来给大家演示一下:
代码展示:
package Time;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//线程池的使用
public class ThreadDemo3 {
public static void main(String[] args) {
//创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
//添加任务到线程池中
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
}
}
结果展示:
说道这里我们就不得不去看一下Java的官方文档中对线程池的解释了。
我打开Java的官方文档,没有的同学可以点击这个链接进入☞Java Platform SE 8
进入之后点击下面的进入ThreadPoolExecutor的页面。
我们可以看到它的参数有以下几个:
我们分别给大家解释一下:
关于线程池的拒绝策略,标准库给我们提供了四种。
下面来给大家分别解释一下:
下面我们就自己来实现一个线程池。
代码展示:
package Time;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
//自己实现的线程池
class MyThreadPool{
//阻塞队列用来存放任务
private BlockingDeque queue = new LinkedBlockingDeque<>();
public void submit(Runnable runnable) throws InterruptedException {
queue.put(runnable);
}
//此处实现一个固定线程数的线程池
public MyThreadPool(int n) {
for (int i = 0; i < n; i++) {
Thread t = new Thread(() -> {
try {
while (true) {
//此处需要让线程池内部有一个while循环,不停的取任务
Runnable runnable = queue.take();
runnable.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
}
}
}
public class ThreadDemo4 {
public static void main(String[] args) throws InterruptedException {
MyThreadPool pool = new MyThreadPool(10);
for (int i = 0; i < 1000; i++) {
int number = i;
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello " + number);
}
});
Thread.sleep(3000);
}
}
}
此处我们可以看到,线程池中任务的执行顺序和添加顺序不一定是相同的,这是非常正常的,因为这是个线程的调度是无序的。
这节小编就与大家分享到这里啦,这节中小编主要与大家分享了什么是线程池,带着大家一起看了标准库中的线程池,并且我们自己还实现了一遍,希望这节对大家线程池有一定了解,想要学习的同学记得关注小编和小编一起学习吧!如果文章中有任何错误也欢迎各位大佬及时为小编指点迷津(在此小编先谢过各位大佬啦!)