(面试必看!)一些和多线程相关的面试考点
文章导读
- 引言
- 考点1. CAS 指令(重点)
- 一、什么是CAS
- 二、CAS 的优点
- 三、CAS 的缺点
- 四、ABA问题
- 五、相关面试题
- 考点2. 信号量(semaphore)
- 一、基本概念
- 二、信号量的主要操作
- 三、信号量的应用
- 四、相关面试题
- 考点3、CountDownLatch 类
- 一、主要用途
- 二、主要方法
- 三、示例
- 考点4、Callable 接口
- Callable 与 Runnable 的主要区别
- 使用场景
- 示例
- 相关面试题
- 考点5、多线程下的数据结构
- 一、多线程环境使用ArrayList
- 二、多线程环境下使用哈希表
- 1、Hashtable
- 2、ConcurrentHashMap(重点)
- 相关面试题
- 考点五、其他常见面试题
引言
本篇文章总结了多线程中面试频率比较高的考点,内容可能比较琐碎,但是如果能够坚持看完,注意总结积累,相信对面试会有很大帮助。多线程内容较多,用一篇文章写完可能篇幅过长,我打算用两篇文章来总结,本篇主要写的是多线程中辅助加锁的数据结构和指令,下一篇主要讲的是锁策略。
考点1. CAS 指令(重点)
一、什么是CAS
CAS(Compare-and-Swap)是一种用于实现多线程同步的原子指令。它涉及到三个操作数:内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值,这个操作是原子的。
CAS 操作包含三个关键动作:
- 比较(Compare):将内存位置的值与预期原值进行比较。
- 交换(Swap):如果比较相等,那么处理器会自动将该内存位置的值更新为新值。
- 原子性(Atomicity):上述整个比较和交换的操作是原子的,即该操作在执行过程中不会被其他线程的操作打断。
CAS 指令一般是基于硬件实现的,在 Intel 处理器中,它可以通过 LOCK 前缀的 CMPXCHG 指令来实现。在 Java 中,CAS 操作被广泛用于实现非阻塞算法,如原子变量类(java.util.concurrent.atomic
包下的类)中的 getAndIncrement
、compareAndSet
等方法都是基于 CAS 实现的。
二、CAS 的优点
- 非阻塞算法:CAS 允许线程在不进入阻塞状态的情况下进行并发操作,这有助于减少线程切换的开销,提高系统的并发性能。
- 无需使用锁:在多个线程竞争同一个资源时,传统的锁机制可能会导致线程阻塞,而 CAS 可以在不依赖锁的情况下实现线程间的同步。
三、CAS 的缺点
- 循环时间长开销大:如果 CAS 操作一直不成功,那么线程会一直处于自旋状态(返回失败并持续重试),这会增加 CPU 的负担。
- 只能保证一个共享变量的原子操作:当需要对多个共享变量进行操作时,CAS 就无法保证操作的原子性了。这种情况下,需要使用锁或其他同步机制来保证操作的原子性。
四、ABA问题
-
情景:
- 假设小明有存款1000元,他去ATM机上取100元,服务器产生了两个线程处理。线程1执行完存款变为900,线程2CAS指令比较失败,无法执行。
- 就在小明取钱的时候,小明的爸爸给小明的银行账户转了100元,产生了线程3。如果线程3是在线程1执行后才产生的,那么就会出现存款从 1000 -> 900 -> 1000 的过程,于是再执行线程2的CAS指令就会成功。本来小明只想取100元,现在取钱操作执行了两次,取出了200元!
- 假设小明有存款1000元,他去ATM机上取100元,服务器产生了两个线程处理。线程1执行完存款变为900,线程2CAS指令比较失败,无法执行。
-
定义:
在CAS操作中,线程会首先读取某个内存位置的值(我们称之为预期值A),然后执行CAS操作,尝试将该内存位置的值修改为新的值(我们称之为B),但前提是内存位置的值必须仍然是预期值A。如果在读取值和尝试修改值之间,有其他线程修改了该内存位置的值(比如从A改为了B,然后又改回了A),那么CAS操作会错误地认为该值没有变化,从而成功执行,这就会导致ABA问题。
在以上情景中,存款从1000变900再变成1000的过程所导致的取钱两次的BUG就是ABA问题。
-
解决方法:
给要修改的值, 引⼊版本号. 在 CAS ⽐较数据当前值和旧值的同时, 也要⽐较版本号是否符合预期。例如:
给存款引入版本号,每次执行线程时版本号加1.
版本号为1,线程1执行扣款成功,存款为900,版本号+1变为2,线程3执行存入成功,存款为1000,版本号+1变为3,线程2执行,版本号与之前读取的不同,执行失败。
五、相关面试题
- 讲解下你⾃⼰理解的 CAS 机制。
- ABA问题怎么解决?
忠告:相关面试题的答案我不会给出,读者应自己总结积累,盲目背诵答案已经过时,面试场上的八股文已被千变万化的情景题目所取代,只有自己总结积累经验和知识才能应对变化,才能让面试官青睐!
考点2. 信号量(semaphore)
一、基本概念
- 定义:信号量是一个非负整数,用于表示某种资源的数量。它有两个主要操作:P(等待)和V(释放)。
- 作用:实现任务之间的同步或临界资源的互斥访问,常用于协助一组相互竞争的任务来访问临界资源。
二、信号量的主要操作
-
P(等待)操作:
- 当一个进程或线程需要访问共享资源时,它会尝试执行P操作。
- 如果信号量的值大于0,表示资源可用,进程或线程可以继续访问资源,并将信号量的值减1。
- 如果信号量的值等于0,表示资源已被占用,进程或线程会被阻塞,直到信号量的值变为正数。
-
V(释放)操作:
- 当一个进程或线程完成对共享资源的访问时,它会执行V操作,将信号量的值加1。
- 如果有其他等待进程被阻塞,它们中的一个将被唤醒并获得对资源的访问权限。
代码示例:
public static void main(String[] args) {// 信号量为4,表明有四个资源待访问Semaphore semaphore = new Semaphore(4);// 写一个线程访问访问资源Thread t = new Thread(() -> {try {// accquire方法表示P操作semaphore.acquire();// do something ...Thread.sleep(1000);// release方法表示V操作semaphore.release();} catch(InterruptedException e){e.printStackTrace();}});t.start();}
三、信号量的应用
信号量在操作系统和并发编程中有着广泛的应用,包括但不限于:
- 进程同步:控制多个进程的执行顺序,保证数据的正确处理。
- 临界资源的互斥访问:保护共享资源,防止数据竞争和冲突。
- 生产者-消费者问题:在生产者-消费者模型中,通过信号量来控制资源的生产和消费。
- 线程池管理:控制线程池中的线程数量,以控制系统的负载。
- 顺序控制:确保多个任务按照特定的顺序执行。
综上所述,信号量是一种重要的同步机制,通过合理地控制信号量的值,可以实现对共享资源的互斥访问和同步操作,从而避免并发编程中的常见问题。
四、相关面试题
- 简单解释一下什么是信号量?
- 什么场景下会使用到信号量?
考点3、CountDownLatch 类
CountDownLatch
是 Java 并发包 java.util.concurrent
中的一个非常有用的同步辅助类,它允许一个或多个线程等待一组其他线程完成操作。这个类通过让一个或多个线程等待其他线程完成一组操作来协调线程。CountDownLatch
初始化时设置一个计数器(count),这个计数器代表等待完成的操作的数量。
一、主要用途
- 等待多个线程完成:
CountDownLatch
允许一个或多个线程等待其他一组线程完成它们的任务。例如,在启动多个线程进行并行计算时,你可能希望等待所有线程都完成计算后再继续执行后续操作。 - 性能优化:通过并行处理,可以提高应用程序的响应速度和吞吐量。
CountDownLatch
可以帮助在并行处理完成后同步后续操作。
二、主要方法
CountDownLatch(int count)
:构造函数,初始化计数器值为给定的count
值。void await()
:使当前线程在锁存器倒计数至零之前一直处于等待状态,除非线程被中断。void await(long timeout, TimeUnit unit)
:使当前线程在锁存器倒计数至零之前一直处于等待状态,或者从当前时间起已经过了指定的等待时间,或者线程被中断。void countDown()
:递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
三、示例
下面是一个简单的示例,展示了如何使用 CountDownLatch
来等待一组线程完成它们的任务。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {int taskCount = 5;ExecutorService executor = Executors.newFixedThreadPool(taskCount);CountDownLatch latch = new CountDownLatch(taskCount);for (int i = 0; i < taskCount; i++) {executor.submit(() -> {try {// 模拟任务执行Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}latch.countDown(); // 完成任务,减少计数器});}// 等待所有任务完成latch.await();System.out.println("所有任务完成");executor.shutdown();}
}
在这个示例中,我们创建了一个包含 5 个任务的线程池。每个任务完成后,都会调用 countDown()
方法来减少 CountDownLatch
的计数器。主线程通过调用 await()
方法等待,直到计数器的值达到 0,即所有任务都已完成。然后,主线程继续执行并打印出 “所有任务完成”。
考点4、Callable 接口
在Java中,Callable
接口是Java并发API的一部分,它位于 java.util.concurrent
包中。与 Runnable
接口不同,Callable
接口可以返回一个结果,并且可能抛出一个异常。这使得 Callable
接口非常适合用于那些需要返回值的并发任务。
Callable
接口的定义如下:
@FunctionalInterface
public interface Callable<V> {/*** Computes a result, or throws an exception if unable to do so.** @return computed result* @throws Exception if unable to compute a result*/V call() throws Exception;
}
Callable 与 Runnable 的主要区别
- 返回值:
Runnable
接口的run
方法没有返回值,而Callable
接口的call
方法可以返回一个泛型类型的值。 - 异常处理:
Runnable
的run
方法不允许抛出受检查的异常(checked exceptions),而Callable
的call
方法可以。如果call
方法抛出了一个异常,这个异常将被封装在一个ExecutionException
中,这个异常是由Future.get()
方法抛出的。
使用场景
当你需要执行一个任务,并且这个任务完成后需要返回一个结果时,就可以使用 Callable
。例如,你可能需要从远程服务器获取数据,或者执行一些计算并返回结果。
示例
创建线程计算 1 + 2 + 3 + … + 1000,
使用 Run 版本:
-
创建⼀个类 Result,包含⼀个 sum 表示最终结果, lock 表⽰线程同步使⽤的锁对象。
-
main ⽅法中先创建 Result 实例, 然后创建⼀个线程 t. 在线程内部计算 1 + 2 + 3 + … + 1000.
-
主线程同时使⽤ wait 等待线程 t 计算结束. (注意, 如果执行到 wait 之前, 线程 t 已经计算完了, 就不必等待了)。
-
当线程 t 计算完毕后, 通过 notify 唤醒主线程, 主线程再打印结果.
public class Demo18 {static class Result {public int sum = 0;public Object lock = new Object();}public static void main(String[] args) throws InterruptedException {Result result = new Result();Thread t = new Thread() {@Overridepublic void run() {int sum = 0;for (int i = 1; i <= 1000; i++) {sum += i;}synchronized (result.lock) {result.sum = sum;result.lock.notify();}}};t.start();synchronized (result.lock) {while (result.sum == 0) {result.lock.wait();}System.out.println(result.sum);}}
}
可以看到,上述代码需要⼀个辅助类 Result,还需要使⽤⼀系列的加锁和 wait notify 操作,代码复杂,容易出错。
使用Callable版本:
- 创建⼀个匿名内部类,实现 Callable 接⼝。 Callable 带有泛型参数,泛型参数表⽰返回值的类型。
- 重写 Callable 的 call ⽅法,完成累加的过程,直接通过返回值返回计算结果.
- 把 callable 实例使⽤ FutureTask 包装⼀下。
- 线程的构造⽅法传⼊ FutureTask。 此时新线程就会执行 FutureTask 内部的 Callable 的 call 方法完成计算,计算结果就放到了 FutureTask 对象中。
- 在主线程中调⽤ futureTask.get() 能够阻塞等待新线程计算完毕. 并获取到 FutureTask 中的结果。
public class Demo18 {public static void main(String[] args) throws InterruptedException, ExecutionException {Callable<Integer> callable = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 1; i <= 1000; i++) {sum += i;}return sum;}};FutureTask<Integer> futureTask = new FutureTask<>(callable);Thread t = new Thread(futureTask);t.start();int result = futureTask.get();System.out.println(result);}
}
可以看到,使⽤ Callable 和 FutureTask 之后,代码简化了很多,也不必⼿动写线程同步代码了。
相关面试题
请你说说Callable 和 Runnable 的主要区别?
考点5、多线程下的数据结构
一、多线程环境使用ArrayList
- 普通的 ArrayList 线程并不安全,在使用时必须在可能发生冲突的地方加锁,操作复杂且容易发生死锁。
- (使用较多)使用
Collections.synchronizedList(new ArrayList)
,synchronizedList 是标准库提供的⼀个基于 synchronized 进行线程同步的方法,它是 Collections 类的一个静态方法,它的返回值是一个对关键方法加了锁的链表(List)。这样可以简化程序猿对代码的加锁操作,降低死锁的风险。 - (常考)使⽤
CopyOnWriteArrayList
,这是一个写时复制的容器。-
原理:
- 当我们往⼀个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出⼀个新的容器,然后新的容器里添加元素;
- 添加完元素之后,再将原容器的引用指向新的容器。
-
优点:
线程可以对CopyOnWrite原容器进行并发的读,而不需要加锁,因为原容器不会添加任何元素,读和写是不同的容器。在读多写少的场景下,性能很高,不需要加锁竞争。
-
缺点:
占用内存较多且新写的数据不能被第⼀时间读取到。
-
二、多线程环境下使用哈希表
HashMap 本身是线程不安全的,Java 又在 HashMap 的基础上封装了两个类:
- Hashtable
- ConcurrentHashMap
1、Hashtable
Hashtable 只是在 HashMap 的基础上,把关键方法加上了锁(synchronized),如:
public synchronized V get(Object key)
public synchronized V put(K key, V value)
这无疑是给所有读写操作都加上了锁,线程想要访问同一个 Hashtable 的任何数据都会直接造成锁竞争(一把锁锁上整个Hash表,如图,一个每次只能有一个线程访问该表),一旦触发扩容,就只有在单个线程(触发扩容的线程)上进行,涉及大量的数据拷贝,效率非常低。
2、ConcurrentHashMap(重点)
ConcurrentHashMap
是 Java 并发包 java.util.concurrent
中的一个非常重要的类,用于在并发环境下替代传统的 HashMap
。它提供了比 Hashtable
更高的并发级别,因为 Hashtable
是同步的,这意味着在每一次访问时,整个表都需要被锁定,这大大降低了并发性能。ConcurrentHashMap
通过以下几个方面的优化和改进来提升并发性能:
-
分段锁(Segmentation Locking)(在 Java 8 之前):
- 在 Java 8 之前,
ConcurrentHashMap
使用分段锁的机制来减少锁的竞争。它将整个哈希表分为多个段(Segment),每个段都维护着自己的锁。这样,在并发环境中,只要多个线程访问的是不同的段,它们就可以并行地执行,从而减少了锁的争用。每个段内部都维护了一个哈希表,用于存储键值对。 - 当需要对某个键进行操作时,首先通过哈希码确定该键属于哪个段,然后只锁定该段进行操作,而不是锁定整个表。
- 在 Java 8 之前,
-
锁粒度细化(Fine-grained Locking)(在 Java 8 及以后):
- 从 Java 8 开始,
ConcurrentHashMap
放弃了分段锁的设计,转而采用了一种更为灵活的锁策略,即使用Node
数组加上链表或红黑树(在链表过长时)的方式来存储键值对,并通过synchronized
关键字或CAS
(Compare-And-Swap)操作来确保线程安全。 - 在 Java 8 的实现中,锁被细化到了每个桶(bucket)上,即每个数组元素。当多个线程访问不同的桶时,它们可以并行地执行。这进一步减少了锁的竞争,提高了并发性能。
- 读操作没有加锁(但是使用了 volatile 保证从内存读取结果),只对写操作进行加锁。
- 从 Java 8 开始,
-
使用 CAS(Compare-And-Swap)操作:
- CAS 是一种无锁算法,用于实现线程间的同步,而不需要使用传统的锁。在
ConcurrentHashMap
的实现中,当尝试修改某个桶(或节点)时,会尝试使用 CAS 操作来更新该桶的状态。如果桶的状态在此期间没有被其他线程修改,则 CAS 操作成功,否则重试(CAS与版本号结合)。 - CAS 操作减少了锁的使用,从而提高了性能,但也可能导致更高的 CPU 使用率,因为需要不断重试直到成功为止。
- CAS 是一种无锁算法,用于实现线程间的同步,而不需要使用传统的锁。在
-
动态扩容:
- 定义:
ConcurrentHashMap
支持动态扩容,即当哈希表中的元素数量达到某个阈值时,会自动进行扩容操作,以避免哈希冲突和性能下降。与HashMap
类似,扩容操作涉及到重新计算每个元素的哈希码,并将其放置到新的哈希表中。但ConcurrentHashMap
的扩容操作是并发安全的,可以在不阻塞读操作的情况下进行。 - 原理:
- 发现需要扩容的线程,只需要创建⼀个新的数组,同时只搬几个元素过去。
- 扩容期间, 新老数组同时存在,后续每个来操作 ConcurrentHashMap 的线程,都会参与搬家的过程,每个操作负责搬运一小部分元素,搬完最后⼀个元素再把老数组删掉。
- 这个期间,插入的元素只往新数组里添加,查找需要同时查新数组和⽼数组。
- 定义:
-
红黑树优化:
- 在 Java 8 及以后的版本中,当某个桶中的链表长度超过一定阈值时(默认为 8),
ConcurrentHashMap
会将该链表转换为红黑树,以优化查找性能。这是因为红黑树在查找、插入和删除操作上的时间复杂度比链表更低(在平均和最坏情况下都是 O(log n)),可以进一步提高并发性能。
- 在 Java 8 及以后的版本中,当某个桶中的链表长度超过一定阈值时(默认为 8),
综上所述,ConcurrentHashMap
通过分段锁(在 Java 8 之前)、锁粒度细化(在 Java 8 及以后)、CAS 操作、动态扩容和红黑树优化等多种机制来优化和改进并发性能,使其成为 Java 中处理并发哈希表的首选数据结构。
相关面试题
- ConcurrentHashMap的读是否要加锁,为什么?
- 介绍下 ConcurrentHashMap的锁分段技术?
- ConcurrentHashMap在jdk1.8做了哪些优化?
- Hashtable和HashMap、ConcurrentHashMap 之间的区别?
考点五、其他常见面试题
以下的面试题的答案都在我之前的文章中,大家可以从中寻找答案,这里我就不再一一赘述。
-
谈谈 volatile关键字的用法?
参考文章 线程安全
-
Java多线程是如何实现数据共享的?
JVM 把内存分成了这几个区域; ⽅法区, 堆区, 栈区, 程序计数器。 其中堆区这个内存区域是多个线程之间共享的。只要把某个数据放到堆内存中,就可以让多个线程都能访问到。
-
Java创建线程池的接⼝是什么?参数 LinkedBlockingQueue 的作用是什么?
参考文章 线程池的认识和使用
-
Java线程共有几种状态?状态之间怎么切换的?
参考文章
线程安全
Thread类和线程的用法 -
Thread和Runnable的区别和联系?
参考文章 Thread类和线程的用法
相关文章:

(面试必看!)一些和多线程相关的面试考点
文章导读 引言考点1. CAS 指令(重点)一、什么是CAS二、CAS 的优点三、CAS 的缺点四、ABA问题五、相关面试题 考点2. 信号量(semaphore)一、基本概念二、信号量的主要操作三、信号量的应用四、相关面试题 考点3、CountDownLatch 类…...

从零到一使用 Ollama、Dify 和 Docker 构建 Llama 3.1 模型服务
本篇文章聊聊,如何使用 Ollama、Dify 和 Docker 来完成本地 Llama 3.1 模型服务的搭建。 如果你需要将 Ollama 官方不支持的模型运行起来,或者将新版本 llama.cpp 转换的模型运行起来,并且想更轻松的使用 Dify 构建 AI 应用,那么…...

【React】详解 React Router
文章目录 一、React Router 的基本概念1. 什么是 React Router?2. React Router 的主要特性 二、React Router 的核心组件1. BrowserRouter2. Route3. Link4. Switch 三、React Router 的使用方法1. 安装 React Router2. 定义路由组件3. 配置路由4. 启动应用 四、Re…...

微软蓝屏”事件暴露了网络安全哪些问题?
📢博客主页:https://blog.csdn.net/2301_779549673 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📢本文由 JohnKi 原创,首发于 CSDN🙉 📢未来很长&#…...

upload-labs靶场练习
文件上传函数的常见函数: 在PHP中,文件上传涉及的主要函数包括move_uploaded_file(), is_uploaded_file(), get_file_extension(), 和 mkdir()。这些函数共同协作,使得用户可以通过HTTP POST方法上传文件,并在服务器上保存…...

java使用hutool工具判断ip或者域名是否可用,java使用ping判断ip或者域名是否可用
1.导入hutool工具 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.16</version></dependency>2.复制以下代码直接运行 import cn.hutool.core.net.NetUtil;public class Test {p…...

apache2和httpd web服务器
apache2和httpd web服务器 apache2和httpd web服务器是啥apache是软件基金会apache2是一个web服务httpd和apache2是同一个东西,但是不同linux发行版中叫法不一样。就是同一个东西,但是看上去有一些不一样。 apache2和httpd web服务器是啥 apache是软件基…...

基于多种机器学习的豆瓣电影评分预测与多维度可视化【可加系统】
有需要本项目的代码或文档以及全部资源,或者部署调试可以私信博主 在本研究中,我们采用Python编程语言,利用爬虫技术实时获取豆瓣电影最新数据。通过分析豆瓣网站的结构,我们设计了一套有效的策略来爬取电影相关的JSON格式数据。…...

Linux系统配置STM32的开发环境(代码编辑,编译,下载调试)
常见的stm32开发都是直接使用keil-MDK工具的,这是个集成开发环境,包含了代码编辑,编译,下载,调试,等功能,而且keil还是个图形化操作工具,直接可以点击图标案件就可以实现编译下载啥的…...

每日一题——第三十五题
题目:有一个文本文件numbers.txt,其中有20个整数,每个整数占一行,编写程序将这些整数从小到大顺序排好后,重新写入到该文件中, 要求排序前和排序后都要输出该文件的内容。 #include<stdio.h> #inclu…...

Echarts 柱状图实现同时显示百分比+原始值+汇总值
原始效果:柱状图 二开效果: 核心逻辑 同时显示百分比和原始值 label: {show: true,position: inside,formatter: (params) > {const rawValue rawData[params.seriesIndex][params.dataIndex];const percentage Math.round(params.value * 1000) / …...

嵌入式学习Day13---C语言提升
目录 一、二级指针 1.1.什么是二级指针 2.2.使用情况 2.3.二级指针与数组指针 二、指针函数 2.1.含义 2.2.格式 2.3.注意 2.4.练习 三、函数指针 3.1.含义 3.2.格式 3.3.存储 3.4.练习 编辑 四、void*指针 4.1.void缺省类型 4.2.void* 4.3.格式 4.4.注…...

Mysql随记
1.对表mysql.user执行DML语句(数据操作语言),那么此时磁盘数据较新,需要手动执行flush privileges 语句来覆盖内存中的授权数据。其他的DDL(数据操作语言),DQL(数据查询语言),DCL(数…...

wire和reg的区别
在 Verilog 中,wire 和 reg 是两种不同的数据类型,用于表示信号或变量。它们在 Verilog 中的使用场景和行为有一些区别: ### wire: - wire 类型用于连接组合逻辑电路中的信号,表示电路中的连线或信号传输线。 - wire …...

c语言第四天笔记
关于 混合操作,不同计算结果推理 第一种编译结果: int i 5; int sum (i) (i) 6 7 13 第二种编译结果: int i 5; int sum (i) (i) 6 7 7 7 前面的7是因为后面i的变化被影响后,重新赋值 14 第一种编译结果ÿ…...

Hive——UDF函数:高德地图API逆地理编码,实现离线解析经纬度转换省市区(离线地址库,非调用高德API)
文章目录 1. 需求背景数据现状业务需求面临技术问题寻求其他方案 2. 运行环境软件版本Maven依赖 3. 获取离线地址库4. Hive UDF函数实现5. 创建Hive UDF函数6. 参考 1. 需求背景 数据现状 目前业务系统某数据库表中记录了约3亿条用户行为数据,其中两列记录了用户触…...

深入解析PHP框架:Symfony框架的魅力与优势
嘿,PHP开发者们!今天我们要聊一聊PHP世界中的一颗闪亮明星——Symfony框架。无论是初学者还是经验丰富的开发者,Symfony都为大家提供了强大的工具和灵活的特性。那就跟着我一起,来探索这个强大的PHP框架吧! 一、什么是…...

Go语言实战:基于Go1.19的站点模板爬虫技术解析与应用
一、引言 1.1 爬虫技术的背景与意义 在互联网高速发展的时代,数据已经成为新的石油,而爬虫技术则是获取这种“石油”的重要工具。爬虫,又称网络蜘蛛、网络机器人,是一种自动化获取网络上信息的程序。它广泛应用于搜索引擎、数据分…...

5个ArcGIS图源分享
数据是GIS的血液。 我们在《15个在线地图瓦片URL分享》一文中为你分享了15个地图瓦片URL链接,现在再为你分享5个能做ArcGIS中直接加载的图源! 并提供了能直接在ArcMAP和ArcGIS Pro的文件,如果你需要这些ArcGIS图源,请在文末查看…...

科普文:万字梳理31个Kafka问题
1、 kafka 是什么,有什么作用 2、Kafka为什么这么快 3、Kafka架构及名词解释 4、Kafka中的AR、ISR、OSR代表什么 5、HW、LEO代表什么 6、ISR收缩性 7、kafka follower如何与leader同步数据 8、Zookeeper 在 Kafka 中的作用(早期) 9、Kafka如何快…...

Unity UGUI 实战学习笔记(4)
仅作学习,不做任何商业用途 不是源码,不是源码! 是我通过"照虎画猫"写的,可能有些小修改 不提供素材,所以应该不算是盗版资源,侵权删 登录面板UI 登录数据逻辑 这是初始化的数据变量脚本 using System.…...

Python学习和面试中的常见问题及答案
整理了一些关于Python和机器学习算法的高级问题及其详细答案。这些问题涵盖了多个方面,包括数据处理、模型训练、评估、优化和实际应用。 一、Python 编程问题 解释Python中的装饰器(Decorators)是什么?它们的作用是什么…...

Mysql-索引视图
目录 1.视图 1.1什么是视图 1.2为什么需要视图 1.3视图的作用和优点 1.4创建视图 1.5更新视图 1.6视图使用规则 1.7修改视图 1.8删除视图 2.索引 2.1什么是索引 2.2索引特点 2.3索引分类 2.4索引优缺点 2.5创建索引 2.6查看索引 2.7删除索引 1.视图 1.1什么是…...

电子签章-开放签应用
开放签电子签章系统开源工具版旨在将电子签章、电子合同系统开发中的前后端核心技术开源开放,适合有技术能力的个人 / 团队学习或自建电子签章 \ 电子合同功能或应用,避免研发同仁在工作过程中重复造轮子,降低电子签章技术研发要求࿰…...

Ubuntu下设置文件和文件夹用户组和权限
在 Ubuntu 上,你可以使用 chmod 和 chown 命令来设置当前文件夹下所有文件的权限和所有者。 设置权限: 使用 chmod 命令可以更改文件和目录的权限。例如,要为当前文件夹下的所有文件和子目录设置特定权限,可以使用以下命令&#x…...

JavaSE从零开始到精通(九) - 双列集合
1.前言 Java 中的双列集合主要指的是可以存储键值对的集合类型,其中最常用的包括 Map 接口及其实现类。这些集合允许你以键值对的形式存储和管理数据,提供了便捷的按键访问值的方式。 2. HashMap HashMap 是基于哈希表实现的 Map 接口的类,…...

探索 OpenAI GPT-4o Mini:开发者的高效创新工具
探索 OpenAI GPT-4o Mini:开发者的高效创新工具 最近,OpenAI 推出了全新的 GPT-4o Mini 模型,以其出色的性能和极具吸引力的价格,引起了开发者们的广泛关注。作为开发者,你是否已经开始探索这个“迄今为止最具成本效益…...

藏文词典查单词,藏汉双语解释,推荐使用《藏语翻译通》App
《藏语翻译通》App推出了藏文词典、藏汉大词典、新术语等全新在线查单词功能。 藏汉互译 《藏语翻译通》App的核心功能之一是藏汉互译。用户只需输入中文或藏文,即可获得翻译结果。 藏文词典查单词 掌握一门语言,词汇是基础。《藏语翻译通》App内置藏…...

【机器学习基础】初探机器学习
【作者主页】Francek Chen 【专栏介绍】⌈Python机器学习⌋ 机器学习是一门人工智能的分支学科,通过算法和模型让计算机从数据中学习,进行模型训练和优化,做出预测、分类和决策支持。Python成为机器学习的首选语言,依赖于强大的开…...

SpringBoot轻松实现多数据源切换
一.需求背景 项目需要实现在多个数据源之间读写数据,例如在 A 数据源和 B 数据源读取数据,然后在 C 数据源写入数据 或者 部分业务数据从 A 数据源中读取、部分从B数据源中读取诸如此类需求。本文将简单模拟在SpringBoot项目中实现不同数据源之间读取数…...