面试笔记——多线程使用场景

2024-05-06 17:20

本文主要是介绍面试笔记——多线程使用场景,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

线程池使用场景(CountDownLatch, Future)

CountDownLatch
CountDownLatch(闭锁/倒计时锁)用来进行线程同步协作,等待所有线程完成倒计时(一个或者多个线程,等待其他多个线程完成某件事情之后才能执行)。

  • 构造参数用来初始化等待计数值
  • await() 用来等待计数归零
  • countDown() 用来让计数减一

在这里插入图片描述
上图中,给定初始值count = 3,调用await方法来判断count是否为0,若不为0,则将线程挂起等待,当count等于0之后,该线程才能继续执行。T2,T3,T4执行时,它们都调用了countdown(),每一次调用这个方法,都会对count减一。因此,调用了3次之后,T1线程继续执行。

CountDownLatch的使用demo:

import java.util.concurrent.CountDownLatch;public class CountDownLatchDemo {public static void main(String[] args) throws InterruptedException {//初始化了一个倒计时锁 参数为 3CountDownLatch latch = new CountDownLatch(3);new Thread(() -> {System.out.println(Thread.currentThread().getName()+"-begin...");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}//count--latch.countDown();System.out.println(Thread.currentThread().getName()+"-end..." +latch.getCount());}).start();new Thread(() -> {System.out.println(Thread.currentThread().getName()+"-begin...");try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}//count--latch.countDown();System.out.println(Thread.currentThread().getName()+"-end..." +latch.getCount());}).start();new Thread(() -> {System.out.println(Thread.currentThread().getName()+"-begin...");try {Thread.sleep(1500);} catch (InterruptedException e) {throw new RuntimeException(e);}//count--latch.countDown();System.out.println(Thread.currentThread().getName()+"-end..." +latch.getCount());}).start();String name = Thread.currentThread().getName();System.out.println(name + "-waiting...");//等待其他线程完成latch.await();System.out.println(name + "-wait end...");}}

运行结果:

Thread-0-begin...
Thread-1-begin...
main-waiting...
Thread-2-begin...
Thread-0-end...2
Thread-2-end...1
Thread-1-end...0
main-wait end...

使用场景一——批量导入:
项目上线之前,需要把数据库中的数据一次性的同步到es索引库中,数据可能有1000万左右,一次性读取数据肯定不行(oom异常),可以使用线程池的方式导入,利用CountDownLatch来控制,就能避免一次性加载过多,防止内存溢出:
在这里插入图片描述
在这里插入图片描述
使用场景二——数据汇总:
在一个电商网站中,用户下单之后,需要查询数据,数据包含了三部分:订单信息、包含的商品、物流信息;这三块信息都在不同的微服务中进行实现的,可以通过线程池实现,提升查询效率:
在这里插入图片描述
在实际开发的过程中,难免需要调用多个接口来汇总数据,如果所有接口(或部分接口)的没有依赖关系,就可以使用线程池+future来提升性能。

使用场景三——异步线程:
在很多软件中,都提供了搜索功能,并且会记录用户的搜索记录。在实现搜索功能的时候,不能让搜索功能受到保存搜索记录的影响,通常采取异步的方式来保存搜索记录,通过异步线程来实现该功能。当用户输入关键字开始搜索后,正常返回用户搜索的相关数据,再开一个线程来记录用户的历史记录,并把这个新开的线程放到线程池中去执行。

控制方法允许并发访问的线程数量

Semaphore 信号量,是JUC包下的一个工具类,底层是AQS,我们可以通过其限制执行的线程数量。

使用场景:通常用于那些资源有明确访问数量限制的场景,常用于限流 。

Semaphore使用步骤

  • 创建Semaphore对象,可以给一个容量
  • semaphore.acquire(): 请求一个信号量,这时候的信号量个数-1(一旦没有可使用的信号量,也即信号量个数变为负数时,再次请求的时候就会阻塞,直到其他线程释放了信号量)
  • semaphore.release():释放一个信号量,此时信号量个数+1
import java.util.concurrent.Semaphore;public class SemaphoreCase {public static void main(String[] args) {// 1. 创建 semaphore 对象Semaphore semaphore = new Semaphore(3);// 2. 10个线程同时运行for (int i = 0; i < 10; i++) {new Thread(() -> {try {// 3. 获取许可,计数-1semaphore.acquire();} catch (InterruptedException e) {e.printStackTrace();}try {System.out.println("running...");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("end...");} finally {// 4. 释放许可  计数+1semaphore.release();}}).start();}}}

对ThreadLocal的理解

ThreadLocal是多线程中对于解决线程安全的一个操作类,它会为每个线程都分配一个独立的线程副本从而解决了变量并发访问冲突的问题。ThreadLocal 同时实现了线程内的资源共享。

ThreadLocal基本使用:

  • set(value) 设置值——ThreadLocal 自己作为 key,资源对象作为 value,放入当前线程的 ThreadLocalMap 集合中
  • get() 获取值——以 ThreadLocal 自己作为 key,到当前线程中查找关联的资源值
  • remove() 清除值——以 ThreadLocal 自己作为 key,移除当前线程关联的资源值

demo:

public class ThreadLocalTest {static ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) {new Thread(() -> {String name = Thread.currentThread().getName();threadLocal.set("value1");print(name);System.out.println(name + "-after remove : " + threadLocal.get());}, "t1").start();new Thread(() -> {String name = Thread.currentThread().getName();threadLocal.set("value2");print(name);System.out.println(name + "-after remove : " + threadLocal.get());}, "t2").start();}static void print(String str) {//打印当前线程中本地内存中本地变量的值System.out.println(str + " :" + threadLocal.get());//清除本地内存中的本地变量threadLocal.remove();}}

输出:

t1 :value1
t1-after remove : null
t2 :value2
t2-after remove : null

ThreadLocal本质来说就是一个线程内部存储类,从而让多个线程只操作自己内部的值,从而实现线程数据隔离。
在这里插入图片描述
set方法:
在这里插入图片描述

get方法/remove方法:
在这里插入图片描述
ThreadLocal——内存泄漏

Java对象中的四种引用类型:强引用、软引用、弱引用、虚引用

  • 强引用:最为普通的引用方式,表示一个对象处于有用且必须的状态,如果一个对象具有强引用,则GC并不会回收它。即便堆中内存不足了,宁可出现OOM,也不会对其进行回收。
    • User user = new User();
  • 弱引用:表示一个对象处于可能有用且非必须的状态。在GC线程扫描内存区域时,一旦发现弱引用,就会回收到弱引用相关联的对象。对于弱引用的回收,无关内存区域是否足够,一旦发现则会被回收
    User user = new User(); 
    WeakReference weakReference = new WeakReference(user);
    

每一个Thread维护一个ThreadLocalMap,在ThreadLocalMap中的Entry对象继承了WeakReference。其中key为使用弱引用的ThreadLocal实例,value为线程变量的副本(强引用):

static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}
}

在这里插入图片描述
避免ThreaLocal内存泄漏——通过remove方法主动释放key、value。

这篇关于面试笔记——多线程使用场景的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/964905

相关文章

春招面试(驭势科技)

第一次面试(37min) 1.自我介绍 2.狂问项目细节。 无人机获取图片的分辨率,车辆占整张图片的百分比,连续帧进行处理,数据集pascal voc问题,为什么要用yolo算法? 车道线检测讲了很久,Canny,Hough,聚类。Canny这边会遇到什么问题(很多干扰的边缘),从而经过Hough有干扰的线。怎么解决?(相同场景下可以,但是参数调整得出的边缘图像一般还是会有干扰,水平线是可以

苏宁算法岗面试

一面:38min 0.自我介绍 1.hr问了一大堆问题,交换生经历,城市,实习与学校的区别等等。 2.技术问题,熟悉哪些深度学习与机器学习的东西,我说了深度学习的东西,开始问。 3.过拟合和欠拟合的原理和解决方案,过拟合和欠拟合在性能上的结果,举了imagenet当中搞得分类问题,为什么会有这样的指标,loss在训练集和测试集上的操作? 答:区分训练误差和泛化误差。 由于优化算法的目标函数通

Horizon提前批面试凉经

一面:1h 0.自我介绍 1.两个项目介绍(难点,学到了什么。量化算法三步骤,定点浮点的问题,静态图与动态图) (1)还是NNIE项目中的三个坑:malloc动态分配内存的时候对于string类型需要memset初始化,不然的话会产生内存不对齐的情况,发生段错误;还有运行mobilenetv1/v2的时候,因为原始的代码中输出FC层的维度是W,结果用imagenet数据集出来只有1维,而不是100

Spark的应用场景有哪些?

Spark 是一种与 Hadoop 相似的开源集群计算环境,是专为大规模数据处理而设计的快速通用的计算引擎,现已形成一个高速发展应用广泛的生态系统,主要应用场景如下:   1. Spark是基于内存的迭代计算框架,适用于需要多次操作特定数据集的应用场合。需要反复操作的次数越多,所需读取的数据量越大,受益越大,数据量小但是计算密集度较大的场合,受益就相对较小;   2. 由于RDD的特性,Spa

使用Array实现Java堆栈

本教程给出了使用Array 实现Stack数据结构的示例。堆栈提供将新对象放在堆栈上(方法push())并从堆栈中获取对象(方法pop())。堆栈根据后进先出(LIFO)返回对象。请注意,JDK提供了一个默认的Java堆栈实现作为类java.util.Stack。 适用于所有堆栈实现的两个强制操作是: push():数据项放置在堆栈指针指向的位置。pop():从堆栈指针指向的位置删除并返回数据

Spring Boot - 使用类类型信息获取所有已加载的bean

Spring启动会在内部加载大量bean,以最少的配置运行您的应用程序。在这个例子中,我们将学习如何找出所有那些Spring boot加载的bean及其类类型信息。 使用ApplicationContext获取所有已加载的bean 要自动执行方法,当应用程序完全加载时,我正在使用CommandLineRunner接口。CommandLineRunner用于指示bean 在Spring应用程序中

使用ThreadPoolExecutor创建线程池有哪些关键参数

1、ThreadPoolExecutor类的全参数构造方法: /*** Creates a new {@code ThreadPoolExecutor} with the given initial* parameters.** @param corePoolSize the number of threads to keep in the pool, even* if they

关于Java的数组的使用

关于一维数组的使用 代码示例一如下: package com;public class test_array {public static void main(String[] args){//1.如何定义 一个 数组//1.1数组的声明String[] names;int[] scores;//1.2数组的初始化://1.2.1静态初始化:初始化数组与数组元素赋值同时进行nam

Android_03_数据库的使用总结

前言: 1>区分SQL和SQLite SQL 是一门 ANSI 的标准计算机语言,用来访问和操作数据库系统。SQL 语句用于取回和更新数据库中的数据。 SQL 可与数据库程序协同工作,比如 MS Access、DB2、Informix、MS SQL Server、Oracle、Sybase 以及其他数据库系统。 不幸地是,存在着很多不同版本的 SQL 语言,但是为了与 ANSI 标准相

Android_02_关于SharePreferences的使用

前言: 我们使用SharePreferences的主要目的是针对一些简单的数据进行存取,其是通过键值对来存取的, 其实质是通过xml文件进行保存的;对于一些简单数据的存取,我们可以用SharePreferences,替代 其他几种复杂的数据存取的方式,比如文件的读写或者数据库的操作; 本示例演示的内容是:通过SharedPreferences来实现记住密码的功能,无需第二次再输入密码