文章目录
- 1、线程池存在的意义
- 2、什么是线程池?
- 3、线程池的使用
- 2、java标准库中的线程池
- 3、认识一下不同的线程池:
- 4、认识一下线程池里的参数:
- 4、实现一个简单的线程池
1、线程池存在的意义
线程存在的意义:使用进程来实现并发编程,造成资源浪费等,因此引入了线程,使用线程来实现并发编程,线程也叫“轻量级进程”,因为 创建、销毁、调度线程比创建、销毁、调度进程更高效;
但是当我们需要频繁的创建、销毁线程时,就会发现开销还是挺大的,因此我们引入了线程池;
2、什么是线程池?
线程池:顾名思义就是“存放线程的池子”;
3、线程池的使用
事先把线程创建好,放到“池”中,后面需要使用的时候直接从“池”里获取即可;如果线程使用完毕,接着放回“池”里面;
注意:从“池”中取出,放入线程比创建销毁线程更高效;因为前者是用户代码实现的,后者是操作系统内核完成的;
什么是操作系统内核?
内核是操作系统的内部核心程序,它向外部提供了对计算机设备的核心管理调用。我们将操作系统的代码分成2部分。内核所在的地址空间称作内核空间。而在内核以外的统称为外部管理程序,它们大部分是对外围设备的管理和界面操作。外部管理程序与用户进程所占据的地址空间称为外部空间。通常,一个程序会跨越两个空间。当执行到内核空间的一段代码时,我们称程序处于内核态,而当程序执行到外部空间代码时,我们称程序处于用户态;
概念有点抽象。举个例子:
去面馆吃饭,你正在吃面的时候突然 想喝冰红茶了,你有两种选择,第一种:自己到冰箱里面拿冰红茶,也就是上面说的“用户态”;第二种:让老板给你拿一瓶冰红茶送到你这里来;也就是上面说的“内核态”;
相比于内核态来说,用户态的程序执行行为是可控的,就拿上述的面条例子来说,自己直接跑到冰箱拿冰红茶,就一定可以很快喝到冰红茶,但是如果让老板呢冰红茶送过来,什么时候可以喝到冰红茶就是不可预知的了,因为老板可能正在忙其他事情,等他忙完了才可以去拿冰红茶。因此,当使用系统调用,执行内核代码的时候,无法确定内核都要做哪些工作,整体的行为是不可控的;
2、java标准库中的线程池
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(10);//构造了有10个线程的线程池
for (int i = 0; i < 100; i++) {
int n = i;
//将100个任务放到池子里,由池子里的线程来分配任务
pool.submit(new Runnable() {
@Override
public void run() {
System.out.print(n + " ");
}
});
}
}
注意:1、当前是往线程池里放了100个任务,这100个任务又是由10个线程来平均分配的(并非严格的平均分配,某一个线程多几个任务也是正常的);
2、我们会发现,main线程结束了,但是整个进程还是没结束,因为线程池里的线程都是前台线程,而前台线程会阻止进程结束
问题一:为什么要将i的值保存起来呢?
这里的i是main线程的局部变量(即i在主线程的栈上),但是随着主线程代码的结束,主线程的栈就销毁了,如果主线程销毁的时候当前任务的在线程池里还没排到,但是此时的i就已经销毁了,所以将i的值在当前run的栈上也拷贝了一份(即n)
3、认识一下不同的线程池:
4、认识一下线程池里的参数:
4、实现一个简单的线程池
class myThreadPool {
private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
public myThreadPool(int k) {//创建线程,执行任务
for(int i = 0;i < k;i++){
Thread thread = new Thread( () -> {
while(true){
try{
Runnable runnable = queue.take();//将任务从队列里拿出
runnable.run();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
thread.start();
}
}
public void submit(Runnable runnable) throws InterruptedException {
queue.put(runnable);//将任务放到队列里面
}
}
public class Test7 {
public static void main(String[] args) throws InterruptedException {
myThreadPool mythreadpool = new myThreadPool(10);//构造有10个线程的线程池
for (int i = 0; i < 100; i++) {
int n = i;
mythreadpool.submit(new Runnable() { //创建任务
@Override
public void run() {
System.out.println(n);
}
});
}
}
}