Ervin

真正的强者是认清了生活的本质,并且去热爱他的人。


  • 首页

  • 标签

  • 分类

  • 归档

  • 搜索

线程池的一些知识点回顾

发表于 2022-09-10 | 分类于 技术
字数统计: 985 | 阅读时长 ≈ 3

对线程池的一些理解

  1. 线程池是对线程的管理和调度,我们自己当然也可以去新建线程去处理并发操作,但是线程池提供了很好的解决线程之间调度,管理的能力,这些能力可能比我们自己去实现更优。
  2. 创建线程池有两种方式(本质上是一种),如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //通过Executors封装好的线程池创建方法去创建
    Executors.newSingleThreadExecutor();
    Executors.newFixedThreadPool(10);

    //使用ThreadPoolExecutor来创建,其实上一种本质上也是调用这个来创建线程池的(阿里规范推荐)
    ExecutorService executors = new ThreadPoolExecutor(1,
    1,
    0L,
    TimeUnit.MILLISECONDS,
    new ArrayBlockingQueue<>(1),
    new ThreadPoolExecutor.DiscardPolicy());
    //其中参数1,为核心线程数;2,最大线程数;3,线程存活时间;4,线程的阻塞队列;5,线程(任务)熔断机制
  3. 基于第二点解释这些参数的意义

  • a, 核心线程数是线程池初始化就会创建的线程,并且该线程不会被回收;
  • b, 最大线程数是线程池中允许存在的最大线程数量;
  • c, 线程存活时间指的是当非核心线程在处理完任务不再使用后多久被线程池回收;
  • d, 线程阻塞队列是任务队列,线程处理的任务会按照队列的特点存在队列中;
  • e, 线程熔断机制是当线程处理不过来任务时,应该做一些什么策略。
  1. 线程池运行的流程
  • 线程池刚创建的时候,里面没有一个线程,任务队列是做为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
  • 当调用execute()方法添加一个一个任务时,线程池会做几个判断
    • a,如果正在运行的线程数量小于核心线程数,则马上创建线程执行这个任务
    • b,如果正在运行的线程数量大于或等于最大线程数,则把任务放入队列中
    • c,如果这时候队列满了,运行的线程数小于最大线程数,则创建线程立马执行这个任务
    • d,如果队列满了,运行的线程数大于等于最大线程数,则执行线程熔断机制
  • 当一个线程完成任务时,它会从队列中取下一个任务来执行
  • 当一个线程没有任务执行,超过线程存活时间时,线程池会判断,如果当前运行线程大于核心线程数,那么这个线程就会被回收。所以最终线程池的线程数量会维持为核心线程的数量。

这样的过程说明,并不是先加入任务就一定先执行,看下面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ExecutorService executors = new ThreadPoolExecutor(3,
6,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(10),
new ThreadPoolExecutor.DiscardPolicy());
//ThreadTest01 test01 = new ThreadTest01();
for (int i = 1; i < 21; i++) {
int taskId = i;
Runnable task = () -> {
try {
System.out.println("thread-" + Thread.currentThread().getName() + " is saving data:" + taskId);
/*模拟存储数据耗时*/
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
executors.execute(task);
}
/*运行结果:首先核心线程为3所以立马执行任务1,2,3;然后任务4到13被放入到队列中,14,15,16又会被新创建的3个线程(最大为6)执行,
再增加任务17到20时由于当前已经有6个线程在处理任务了,所以17到20的任务由于ThreadPoolExecutor.DiscardPolicy()策略会被丢弃。
然后6个线程又会依次从队列冲取出4到13任务去执行。*/

阅读全文 »

Java反射机制

发表于 2022-06-28 | 分类于 技术
字数统计: 207 | 阅读时长 ≈ 1

什么叫反射

Java中我们创建对象一般都是用“new”这个关键字的,例如

1
2
Person p = new Person();
p.setName("Ervin");

反射则是一开始不知道要初始化的对象是什么,所以没办法new出一个对象,这时候就需要用到反射,例如我们使用第三方SDK的时候有可能没有提供api,只能通过反射去获取类中的方法等。按照上面生成对象的例子,使用反射是这样的

1
2
3
4
5
6
Class cls = Class.forName("com.ervin.entity.Person");
Method method = cls.getMethod("setName", String.class);
//Object person = cls.newInstance();
Constructor constructor = cls.getConstructor();
Object person = constructor.newInstance();
method.invoke(person, "Ervin");

直接new对象是静态模式,在代码编译前就知道需要生成什么对象;反射是在运行时才知道要生成什么类,是动态的

怎么看懂UML类图

发表于 2022-05-19 | 分类于 技术
字数统计: 1.1k | 阅读时长 ≈ 3

从一个例子开始

请看一下这个类图,类之间的关系是我们需要关注的:
UML例图

  • 车的类图结构为《abstact》,表示车是一个抽象类;
  • 车有两个子类小汽车和自行车,它们之间的关系为实现关系,使用带空心箭头的虚线表示;
  • SUV是小汽车的一种,继承自小汽车,他们之间是泛化关系,使用带空心箭头的实线表示;
  • 小汽车和轮胎和发动机为组合关系,表示轮胎是小汽车的一部分,使用带实心箭头的实线表示;
  • 学生和班级之间是聚合关系(下面会介绍和组合关系的区别),是用带空心箭头的实线表示;
  • 学生和身份证之间为关联关系(下面会介绍和组合关系的区别),使用一根实线表示;
  • 学生上学需要用到自行车,与自行车是一种依赖关系,使用带箭头的虚线表示;
阅读全文 »

消息推送

发表于 2021-06-18 | 分类于 技术
字数统计: 9 | 阅读时长 ≈ 1
私有文章, 请输入密码查看。
阅读全文 »

私有文章

发表于 2021-06-18 | 分类于 技术
字数统计: 9 | 阅读时长 ≈ 1
有东西被加密了, 请输入密码查看.
阅读全文 »

聊一聊LRU算法及LinkedHashMap

发表于 2020-07-23 | 分类于 技术
字数统计: 2k | 阅读时长 ≈ 9

概述

以前聊过HashMap的数据结构,其实就是一个数组+链表Android内存优化。HashMap是无序的,因为他是按照key值去存放数据的,key值和你put进去的顺序几乎是不会相同的。那怎么能保证我put进去的数据和get出来的数据是相同顺序呢,这就要用到这里的LinkedHashMap。

阅读全文 »

AtomicBoolean详解

发表于 2020-07-14 | 分类于 技术
字数统计: 783 | 阅读时长 ≈ 3

背景

java中并发的类基本都在java.util.concurrent这个包中,包括前面介绍的大名鼎鼎的AQS。这个AtomicBoolean也是这个包的类,它支持在单个变量上解除锁的线程安全编程。java.util.concurrent.atomic此包中的类可以将volatile值,字段和数组元素的概念扩展到那些也提供院子条件更新操作的类。后续会一个一个研究。

阅读全文 »

八一八强引用、软引用、弱引用、虚引用

发表于 2020-07-13 | 分类于 技术
字数统计: 2.3k | 阅读时长 ≈ 8

背景

我们都知道JVM的垃圾回收机制中,GC判断堆中的对象实例或者数据是不是需要回收(栈里面存放的是基本类型数据及对像的引用,所以是不需要GC去回收)的方法主要有两种:引用计数法和可达性算法两种。而无论是通过引用计数算法判断对象的引用数量,还是通过根搜索算法判断对象的引用链是否可达,判定对象是否存活都与“引用”相关。

阅读全文 »

Kotlin内联函数

发表于 2020-05-30 | 分类于 技术
字数统计: 2k | 阅读时长 ≈ 9

Kotlin中内联函数的理解

kotlin中使用高阶函数会带来一些运行时的效率损失:每一个函数都是一个对象,并且会捕获一个闭包。即那些在函数体内会访问到的变量。内存分配(对于函数对象和类)和虚拟调用会引入运行时间开销。

调用一个方法是一个压栈和出栈的过程,调用方法时将栈针压入方法栈,然后执行方法体,方法结束时将栈针移出栈,这个压栈和出栈的过程会耗费资源,这个过程中传递形参也会耗费资源。

来看一个官方的例子:

1
2
3
4
5
6
7
8
fun <T> lock(l: Lock, body: () -> T): T {
l.lock()
try {
return body()
} finally {
l.unlock()
}
}

调用这个方法:

1
lock(l, {"do something!"})//l是一个Lock对象

对于编译器来说,调用lock方法就要将参数l和lambda表达式{“do something!”}进行传递,还要将lock方法进行压栈出栈处理,这个过程就会耗费资源。如果只要函数体类似这样:

1
2
3
4
5
6
l.lock()
try {
return "do something!"
} finally {
l.unlock()
}

这样做的效果和调用lock方法是一样的,而且不需要压栈出栈了,但是如果代码中频繁调用lock方法,必然要复制大量重复代码,那么有没有一种机制,又能少些重复代码(变成一个可供调用的方法)又不会在调用过程中频繁压栈出栈影响性能呢。有的,这就是kotlin的内联函数inline所拥有的能力。

阅读全文 »

Java线程池实现原理(三)

发表于 2020-05-27 | 分类于 技术
字数统计: 1.5k | 阅读时长 ≈ 5

转自美团技术团队

线程池生命周期管理

上一期讲了线程池生命周期管理的任务管理,对于线程池来说,任务管理和线程管理是它必须关注的事情,而线程管理比任务管理则更复杂。

线程管理

Worker线程(工作线程)

线程池为了掌握线程的状态并维护线程的生命周期,设计了线程池内的工作线程Worker,先看一下源码:

1
2
3
4
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
final Thread thread;//Worker持有的线程
Runnable firstTask;//初始化的任务,可以为null
}

Worker这个线程,实现了Runnable接口,并持有一个线程thread变量,一个初始化的任务firstTask,thread是在调用构造方法时通过ThreadFactory来创建的线程,可以用来执行任务;firstTask用它来保存传入的第一个任务,这个任务可以有也可以为null。如果这个值时非空的,那么线程就会在启动初期立即执行这个任务,也就是对应核心线程(corePool)创建时的情况,如果这个值时null,那么就需要创建一个线程去执行任务列表(workQueue)中的任务,也就是非核心线程的创建。

阅读全文 »
1…345…9
Ervin Zhang

Ervin Zhang

stay hungry, stay foolish

86 日志
3 分类
60 标签
RSS
GitHub E-Mail Twitter
© 2026 Ervin Zhang | Site words total count: 112.2k
本站访客数:次 |
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4