需要提前把线程准备好!!创建线程不是直接从系统申请,而是从池子里拿!
等到线程不用了,也是还给池子!!
池子的目的是为了提高效率
线程的创建虽然比进程轻量,但是在频繁创建的情况下,开销也是不可忽略的!!则希望还能进一步提高效率,那么,就需要用到线程池了!!
为啥从池子里拿线程比从系统创建线程更高效??
原因:从线程池那线程,存粹的用户态操作,从系统创建线程,涉及到用户态和内核态之间的切换,真正的创建,是要在内核态完成的~~
纯用户态操作时间是可控的!
涉及到内核态操作,时间就不太可控了!
标准库里提供了现成的线程池~~
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
ExecutorService pool= Executors.newFixedThreadPool(10);
//此处并非直接new ExecutorService 对象,而是通过Executors类,里面的静态方法,完成对象的构造
//工厂模式:创建对象不在new,而是使用一些其他的方法(通常为静态方法),协助我们把对象创建出来
//Executors.newFixedThreadPool(10)构造一个固定为10线程的线程池
//Executors.newFixedThreadPool()不设置固定值,按需创建,用完了不会立即销毁,留着以后接着用!
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
}
}
需要大家主注意一下笔者的这些备注~
上述的这些工厂方法,其实都是基于一个类:ThreadPoolExecutor的封装(参数很多,还挺抽象的)
总分:多线程代码,还是挺复杂的,稍微不留点神额,就会有问题!
但是,在Java圈子里,多线程仍然是一个比较主流的并发编程模型~
线程池:从线程池取线程,是属于纯用户态操作,不涉及到和内存的交互!!
ThreadPoolExecutor:标准库提供的四种拒绝策略
ThreadPoolExecutor.AbortPolicy:如果满了,继续添加任务,添加操作直接抛出异常
ThreadPoolExecutor.CallerRunsPolicy:添加的线程自己负责执行这个任务
ThreadPoolExecutor.DiscardOldestPolicy:丢弃最老的任务(最先安排的任务)
ThreadPoolExecutor.DiscardPolicy:丢弃最新的任务
接下来,我们来自己实现一个线程池:
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
class MyThreadPool {
//手动实现一个线程池
//阻塞队列
private BlockingDeque<Runnable> queue=new LinkedBlockingDeque<>();
public void submit(Runnable runnable) throws InterruptedException{
queue.put(runnable);
}
//此处实现一个固定线程数的线程池
public MyThreadPool(int n){//n表示线程数量
for (int i = 0; i < n; i++) {
Thread t=new Thread(()->{
try {
while (true){
//while可确保不停的取任务
Runnable runnable=queue.take();
runnable.run();
}
}catch (InterruptedException o){
o.printStackTrace();
}
});
//启动线程
t.start();
}
}
}
public class Main1 {
public static void main(String[] args) throws InterruptedException {
MyThreadPool pool=new MyThreadPool(10);//创建线程池
for (int i = 0; i < 100; i++) {
int number=i;
//匿名内部类,而lambad本质就是匿名内部类的简化写法!!
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello + main");
}
});
}
Thread.sleep(1000);
}
}
根据代码的运行结果:
可以看到:线程池中任务执行的顺序和添加顺序不一定相同!!非常正常!!原因:创建的这10个线程是无序调度的!
问题1:
在上述的代码中:用到了number=i;直接用不行吗??
每次循环都是创建新的number,没有人修改该number,匿名内部类也是要捕获外界变量,而变量捕获要求得是final,而此处的 i 在跟着for循环在不停的修改,因此,创建一个新的变量,让起每次for循环都变成新的!!
问题2:
当前代码中,搞了个10个线程的线程池,在实际开发中,一个线程池的线程数量设置多少是比较合适的??
肯定线程并不是越多越好!!线程的执行本质上是要在CPU上调度的(资源)!最好的做法是做测试的方式来确定!(运行下程序,计算下时间,同时监测资源使用状态)
到此为止:多线程大致结束!!
多线程初阶,主要介绍了线程的概念,及其多线程编程,多线程编程的注意事项,代码案列,算是最核心的部分了!!
面试常考+工作常用!!
多线程进阶(锦上添花),学有余力可以搞搞!!(能理解最好,不能理解就背)
主要围绕一些更深奥的面试题来展开的!