栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

第一节:线程池

第一节:线程池

第一节:多线程与分布式
1-1、周课介绍:
  1. 线程池的基本使用、特点、注意点
  2. ThreadLocal的基本使用、原理、注意事项
  3. 分布式基础、核心概念
  4. Docker的下载、安装和基本使用命令
  5. 独立制作Docker容器
  6. Nginx的安装、基本使用和常用命令
  7. 使用Nginx搭建文件服务
  8. 消息队列RabbitMQ的核心概念queue、message和exchange
  9. RabbitMQ的四种交换机模式
  10. SpringBoot整合RabbitMQ实例
2-1、初识线程池

初始化线程池——线程池,治理线程的法宝

  1. 线程池的自我介绍
  2. 新建和停止线程池
  3. 常见线程池的特点和用法
  4. 任务大多线程,如何拒绝
  5. 钩子方法,给线程池加点料
  6. 实现原理,源码分析
  7. 使用线程池的注意点
  • 线程池的重要性:
  • 什么是池:软件中的“池”,可以理解为计划经济,避免创建线程带来的大开销,可复用线程,可控制资源总量。

如果不使用线程池,每一个任务新开一个线程:

  • 一个线程

EveryoneTaskOneThread.java

package threadpool;

public class EveryTaskOneThread {
    public static void main(String[] args) {
        Thread thread = new Thread(new Task());
        thread.start();
    }
    static class Task implements Runnable{

        @Override
        public void run() {
            System.out.println("执行力任务");
        }
    }
}

  • for循环创建线程
    FoeLoop.java
package threadpool;

public class ForLoop {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new Task());
            thread.start();
        }
    }
    static class Task implements Runnable{

        @Override
        public void run() {
            System.out.println("执行力任务");
        }
    }
}

  • 当数量上升到1000
package threadpool;

public class ForLoop {
    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            Thread thread = new Thread(new Task());
            thread.start();
        }
    }
    static class Task implements Runnable{

        @Override
        public void run() {
            System.out.println("执行力任务");
        }
    }
}

这样开销大,我们希望有固定数量的线程,来执行这1000个线程,这样就避免了反复创建并销毁线程所带来的开销问题。
问题一:反复创建线程开销大 问题二:过多的线程会占用太多内存

解决以上两种问题的思路:

  1. 用少量的线程——避免内存的过多使用
  2. 让这部分线程都保持工作,且可以反复执行任务——避免生命周期的损耗
线程池的好处:
  • 加快响应速度
  • 合理利用CPU和内存
  • 统一管理资源
线程池使用的场景:

服务器接收大量请求时,使用线程池技术是非常合适的,可以大大减少线程的创建和销毁次数,提高服务器的工作效率
实际上,在开发过程中,如果需要创建5个以上的线程,那么就可以使用线程池来管理。

3-1 线程增减的时机
  • 线程池构造方法的参数
  • 线程池是该手动创建还是自动创建
  • 线程池里的线程数量设置多少比较合适
  • 停止线程池的正确方法
参数名类型含义
corePoolSizeint核心线程数
maxPoolSizeint最大线程数
keepAliveTimelong保持存活时间
workQueueBlockingQueue任务存储队列
threadFactoryThreadFactory当线程池需要新的线程的时候,会使用threadFactory来生成新的线程
HandlerRejectedExecutionHandler由于线程池无法接受你所提交的任务的拒绝策略
  • corePoolSize指的是核心线程数——线程池在完成初始化后,默认情况下,线程池中并没有任何线程,线程池等待有任务到来时,再创建线程去执行任务
  • 最大量maxPoolSize-在核心线程数的基础上,额外增加的线程数的上线。
添加线程的规则
  1. 如果线程数小于corePoolSize,创建一个新线程来运行任务。
  2. 如果线程数等于(或大于)corePoolSize但少于maximumPoolSize,则将任务放入队列。
  3. 如果队列已满,并且线程数小于maxPoolSize,则创建一个新线程。
  4. 如果队列已满,且线程数大于或等于maxPoolSize,则拒绝。
是否需要增加线程的判断顺序是:
  • corePoolSize
  • workQueue
  • maxPoolSize
  • 比喻:烧烤店的桌子

举个例子:

  • 线程池:核心池大小为5,最大池大小为10,队列为100.
  • 因为线程中的请求最多会创建5个,然后任务将会被添加到队列中,直到达到100,当队列已满时,将创建最新的线程maxPoolSize,最多到10个线程,如果再来任务,就拒绝。
增减线程的特点
  1. 通过设置corePoolSize 和 maximumPoolSize 相同,就可以创建固定大小的线程池
  2. 线程池希望保持较少的线程数,并且只有在负载变得很大时才增加它。
  3. 通过设置maximumPoolSize为很高的值,就可以允许线程池容纳任意数量的并发任务。
  4. 只有在队列填满时才创建多于corePoolSize的线程,如果使用的是无界队列,那么线程数就不会超过corePoolSize。
3-2 线程存活时间和工作队列 keepAliveTime

如果线程池当前的线程数多于corePoolSize,那么多余的线程空闲时间超过keepAliveime,他们就会被终止

ThreadFactory 用来创建线程
  • 默认使用Executors.defaultThreadFactory()
  • 创建出来的线程都在同一个线程组
  • 如果自己指定threadFactory,那么就可以改变线程名、线程组、优先级、是否是守护线程等等
workQueue 工作队列

常见3种队列类型:

  1. 直接交接: SynchronousQueue
  2. 无界队列: linkedBlockingQueue
  3. 有界对列: ArrayBlochingQueue
3-4 守护线程

作用:给用户线程提供服务
在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程) ,分类标准是线程是否
会阻止JVM的停止——只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部
继续工作;只有当最后一个非守护线程结束时,所有守护线程才会随着JVM一同结束工作。(非守
护线程等同于用户线程)
我们知道,Java虚拟机通常会继续执行线程,直到发生以下两种中的任一情况时,Java程序才能运
行结束:
1.已调用System.exit()方法
2.所有非守护程序线程的线程都已结束
而一般情况下我们不会调用System.exit()方法,所以大部分的Java程序的结束都是由于所有用户线
程都结束而导致的。
所以可以认为,任何一个守护线程都是整个JVM中所有用户线程(非守护线程)的管家。Daemon
的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它是一
个很称职的守护者。
守护线程的特性
线程类型默认继承自父线程
守护线程创建的线程为默认是守护线程,同样,用户线程创建的线程默认为用户线程。非守护线程
如果想创建一个守护线程,需要调用Thread.setDaemon来设置它(Thread类用布尔值daemon属性
来表示线程是否是守护线程),并且,该方法必须在start之前调用,否则会抛出 IllegalThreadState
Exception 异常。
被谁启动?
通常由JVM启动,而不是由用户去启动。当JVM启动时,通常会有一个非守护线程(通常为执行ma
in函数的线程)。
不影响JVM退出
当只剩下守护线程时,JVM就会退出,因为如果只剩下守护线程,就没必要继续运行程序了。
守护线程没结束并不会影响JVM的正常停止:假设所有用户线程都结束了,那么就算有5个守护线程
正在运行,JVM也会正常停止:
守护线程和普通线程的区别
User和Daemon两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果 User Thread已经
全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了,这是因为没有了“被守护
者”,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。
这就是守护线程的作用:告诉JVM不需要等待它退出,当JVM中所有的线程都是守护线程的时候,JV
M就可以正常的退出了。
我们是否需要给线程设置为守护线程?
我们通常不应把自己的线程设置为守护线程,因为设置为守护线程是很危险的。比如线程正在访问
如文件、数据库的时候,所有用户线程都结束了,那么守护线程会在任何时候甚至在一个操作的中
间发生中断,所以守护线程永远不应该去访问固有资源。
代码案例:
定义三个线程类,分别是ThreadA,ThreadB,ThreadC。继承Thread类,重写run()方法,在方法
中循环输出线程名称以及执行次数。其中ThreadA,ThreadB为用户线程,循环输出5次。ThreadC
为守护线程,循环输出20次(次数太少无法观察到效果,可以增加循环次数,例如循环输出50次,
100次等)

线程池手动创建还是自动创建
  • 手动创建更好,因为这样可以更加明确线程池的运行规则,避免资源耗尽的风险
  • 自动创建线程池(即直接调用JDK封存好的方法)可能带来哪些风险?
package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class FixedTreadPoolSize {
    public static void main(String[] args) {
        
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        for (int i = 0; i < 1000; i++) {
            executorService.execute(new Task());
        }
    }
}

class Task implements Runnable{

    @Override
    public void run() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName());
        }
    }
}
newFixedThreadPool

易造成大量内存占用,可能会导致OOM

调整一下,把内存调小一些,“Edit Configuration”->VM Options : -Xmx8m -Xms8m

package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class FixedThredPoolSizeOOM {
    
    private static ExecutorService executorService = Executors.newFixedThreadPool(1);

    public static void main(String[] args) {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            executorService.execute();
        }

    }
}
class SubThread implements Runnable{
    @Override
    public void run() {
        try {
            Thread.sleep(100000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

newFixedThreadPool

当请求堆积时,可能会占用大量的内存

ParameterFixedThreadPoolcachedScheduledSingle
corePoolSizeconstructor_args0constructor-args1
maxPoolSizesame as corePoolSizeInteger.MAX_VALUEInteger.MAX_VALUE1
keepAliveTime0 seconds60 seconds0 seconds0 seconds
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/342848.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号