高级面试题总结—线程池还能这么玩?

前言

面试中我们经常会遇到多线程和线程池的问题,究竟如何回答呢?今天关于Java中的线程池,我们就来学习一下。

什么是线程池

线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。线程池中线程的数量通常完全取决于可用内存数量和应用程序的需求。然而,增加可用线程数量是可能的。线程池中的每个线程都有被分配一个任务,一旦任务已经完成了,线程回到池子中并等待下一次分配任务。

说白了就是为一应用执行多个线程的一个线程集合体

为什么需要线程池?

使用线程池,我目前解决了一下问题:
- 利用线程池, 获取一个视频中的多个封面,从而节约了一些时间和资源
- 后台服务器不支持多张图片上传,利用线程池,进行多张图片上传,从而减少上传的时间

效果图如下:

上传前:9张图片至少要用3秒,利用线程池优化之后, 9张图片要用1秒。

基于以下几个原因在多线程应用程序中使用线程是必须的:

  • 1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

  • 2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

    1. 线程池改进了一个应用程序的响应时间。由于线程池中的线程已经准备好且等待被分配任务,应用程序可以直接拿来使用而不用新建一个线程。
    1. 线程池节省了CLR 为每个短生存周期任务创建一个完整的线程的开销并可以在任务完成后回收资源。
    1. 线程池根据当前在系统中运行的进程来优化线程时间片。
    1. 线程池允许我们开启多个任务而不用为每个线程设置属性。
    1. 线程池允许我们为正在执行的任务的程序参数传递一个包含状态信息的对象引用。
    1. 线程池可以用来解决处理一个特定请求最大线程数量限制问题。

线程池的作用:

线程池作用就是限制系统中执行线程的数量。
根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排 队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池 中有等待的工作线程,就可以开始运行了;否则进入等待队列。

单线程的弊端

举个例子
“`
new Thread(new Runnable() {

@Override
public void run() {
paPaPaYourGridFriend();
}
}).start();

###### 重要的事情说三遍!!!
**如果你还在用new Thread执行一个异步任务,so你就Out了!**
**如果你还在用new Thread执行一个异步任务,so你就Out了!**
**如果你还在用new Thread执行一个异步任务,so你就Out了!**

###### 弊端如下:
- a. 每次new Thread新建对象性能差。
- b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
- c. 缺乏更多功能,如定时执行、定期执行、线程中断。

#### Java 线程池
**1. newSingleThreadExecutor**



创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

**2.newFixedThreadPool**

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

**3. newCachedThreadPool**


创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,

那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

**4.newScheduledThreadPool**

创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。


##### newSingleThreadExecutor

private void TextnewSingleThreadExecutor(){
ExecutorService pool = Executors. newSingleThreadExecutor();

    MyTask1 task1 =   new MyTask1();
    MyTask2 task2 =   new MyTask2();
    MyTask3 task3 =   new MyTask3();

// pool.execute(task1);
// pool.execute(task2);
// pool.execute(task3);

    new Thread(task1).start();
    new Thread(task2).start();
    new Thread(task3).start();
}

private class MyTask1 implements Runnable{
    @Override
    public void run() {
        //循环输出
        for(int i = 0; i < 100; i++)
        {
            System.out.print("A"+i+"\t");
        }

    }
}
private class MyTask2 implements Runnable{
    @Override
    public void run() {

// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//循环输出
for(int i = 0; i < 100; i++)
{
System.out.print(“B”+i+”\t”);
}
}
}
private class MyTask3 implements Runnable{
@Override
public void run() {
//循环输出
for(int i = 0; i < 100; i++)
{
System.out.print(“C”+i+”\t”);
}
}
}
“`

ScheduledExecutorService

newFixedThreadPool


newCachedThreadPool

相比new Thread,Java提供的四种线程池的好处在于:
  • a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
  • b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
  • c. 提供定时执行、定期执行、单线程、并发数控制等功能。

线程池真的是太好用了,如果在项目中通过线程池管理线程,,你将会发现其中的诸多优势!

阅读更多

20+个很棒的Android开源项目

2018年Android面试题含答案–适合中高级(下)一份完整的Android Studio搭建Flutter教程

深入了解JAVA的线程中断方法经验之总结

子线程为什么不能更新UI线程详解

相信自己,没有做不到的,只有想不到的

在这里获得的不仅仅是技术!

你可能感兴趣的:(jvm)