【Java基础】day17

day17

一、为什么一定要发生 STW 事件?

  • 如果没有 STW,会出现浮动垃圾,导致回收性能差、效率低。浮动垃圾是指并发清理阶段,用户线程运行产生的垃圾。
  • 分析工作必须在一个能确保一致性的快照中进行。
  • 一致性指分析期间整个执行系统看起来像被冻结在某个时间点上。
  • 如果出现分析过程中对象引用关系还在不断变化,则分析结果的准确性无法保证,所以需要在执行分析过程前执行快照冻结。

二、什么时候会触发 STW 的 Full GC 事件?

  1. Perm 空间不足;
  2. CMS GC 时出现 promotion failedconcurrent mode failure(concurrent mode failure 发生一般是 CMS 正在进行,但是由于老年代空间不足,需要回收老年代中不再被使用的对象;这时停止所有的线程并终止 CMS,直接进行 Serial Old GC);
  3. 统计得到的 Young GC 晋升到老年代的平均大小大于老年代的剩余空间;
  4. 主动触发 Full GC,执行 jmap -histo:live [pid] 来避免碎片问题。

三、线程池的创建方式有哪些?

总的来说,创建线程池的方式有有两类,七种类型。

  1. 通过 ThreadPoolExecutor 创建的线程池;
  2. 通过 Executors 创建的线程池。

两类创建方式,一共可以创建七种线程类型:

  1. Executors.newFixedThreadPool:创建⼀个固定⼤⼩的线程池,可控制并发的线程数,超出的线程会在队列中等待;
  2. Executors.newCachedThreadPool:创建⼀个可缓存的线程池,若线程数超过处理所需,缓存⼀段时间后会回收,若线程数不够,则新建线程;
  3. Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执⾏顺序;
  4. Executors.newScheduledThreadPool:创建⼀个可以执行延迟任务的线程池;
  5. Executors.newSingleThreadScheduledExecutor:创建⼀个单线程的可以执⾏延迟任务的线程池;
  6. Executors.newWorkStealingPool:创建⼀个抢占式执行的线程池,任务执行顺序不确定,JDK 1.8 添加。
  7. ThreadPoolExecutor:最原始的创建线程池的方式,它包含了 7 个参数可供设置。

在实际生产中,主要推荐使用 ThreadPoolExecutor 来创建线程。这时只需要调用 execute 方法添加一个任务即可,任务的执行方式,根据线程空闲状态、阻塞队列、最大线程数、拒绝策略共同决定。

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler);

Reference
https://blog.csdn.net/qq_41821963/article/details/125341789

四、为什么不推荐使用 Executors 创建线程?

《阿里 Java 开发规约手册》中提到:

【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2) CachedThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

要规避这种风险,就尽量不使用 Executors 来直接创建线程。推荐在实际生产中使用 ThreadPoolExecutor 来创建线程,更便于线程的管理。

产生这条规约的原因:担心团队中有人会一直往 Fixed 类型的线程池中提交任务,导致 OOM 发生。

我们可以使用信号量或者 Latch 来控制任务提交的数量,可以一定程度上在业务层面避免 OOM 情况的发生,但是产生 OOM 的最根本原因在于 Fixed 类型线程池本身的设计与开发者的错误使用。

Reference
https://blog.csdn.net/qq_50652600/article/details/121204469

五、为什么需要使用日志框架?

在 Java 中,System.out.println 方法会将消息写到标准输出流,通常是控制台。这个方法是阻塞的,因为它会等待所有的消息都写入到标准输出流后才继续执行。在线上环境不推荐使用 System.out.println,因为写入到控制台的这些日志信息可能会被丢弃或丢失,且会产生阻塞问题。

相比之下,日志框架可以将日志记录到不同的目的地,例如控制台、文件或数据库。日志框架不会阻塞程序的执行,因为写日志的过程是异步的,不会阻塞程序的执行。日志框架将日志写入到缓冲区中,等待适当的时候再将日志写入到持久化的存储中,这样日志信息就不会丢失,并且可以方便地进行查询和分析。

日志框架通常使用输出器 appender 将日志记录输出到指定的目的地。输出器是日志框架的一个组件,它负责将日志消息写入到目的地。例如,可以使用文件输出器将日志记录写入到文件中,或者使用控制台输出器将日志记录输出到控制台。

在使用日志框架记录日志时,首先需要配置输出器,指定日志记录输出的目的地。然后,可以在代码中使用日志框架提供的 API 来记录日志。例如,在 logback 中可以使用 logger.info 方法来记录信息日志,或者使用 logger.error 方法来记录错误日志。

总结:

  • System.out.println 是阻塞的,会等待所有消息写入到标准输出流才继续执行;且直接将日志输出到控制台,会造成日志信息丢失。
  • 日志框架可以执行日志的持久化操作,且将日志从缓冲区写入到磁盘的操作是异步的,不会造成程序阻塞。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值