JUC下的BlockingQueue详解
BlockingQueue
是Java并发包(java.util.concurrent
)中提供的一个接口,它扩展了Queue
接口,增加了阻塞功能。这意味着当队列满时尝试入队操作,或者队列空时尝试出队操作,线程会进入等待状态,直到队列状态允许操作继续。这种设计模式有效地解决了生产者-消费者问题,确保了线程间的协作和同步,避免了忙等待,提高了系统的效率和响应性。
详细介绍
BlockingQueue
是Java并发编程中处理线程间数据传递的重要工具,通过阻塞机制简化了同步控制,提高了代码的可读性和可靠性。不同的实现提供了多样化的选择,满足不同应用场景的需求。理解和熟练运用BlockingQueue
,是进行高效并发编程的关键之一。
BlockingQueue
是Java并发包(java.util.concurrent
)中的一个重要接口,它是Queue
接口的一个扩展,特别为线程间的协作而设计。它提供了一系列阻塞操作,使得在多线程环境下进行数据的生产和消费变得既安全又高效,是实现生产者-消费者模型的理想工具。
核心特点
-
阻塞特性:当队列为空时,试图从队列中取元素的操作会被阻塞;当队列满时,试图向队列中添加元素的操作也会被阻塞。这样设计避免了使用传统同步机制(如
wait()
和notify()
)的复杂性,使得代码更加简洁且易于理解。 -
线程安全:所有
BlockingQueue
的实现都是线程安全的,多个生产者线程和消费者线程可以安全地并发访问同一个BlockingQueue
实例,而不需要额外的同步控制。 -
公平性:某些
BlockingQueue
的实现允许设置公平性策略,即按照线程等待的顺序分配访问权,这有助于避免饥饿现象。 -
灵活的实现:Java提供了多种
BlockingQueue
的实现,如ArrayBlockingQueue
(基于数组的固定大小队列)、LinkedBlockingQueue
(基于链表的可选固定大小队列)、PriorityBlockingQueue
(支持优先级排序的无界队列)和SynchronousQueue
(直接交换,没有容量的队列)等,开发者可以根据具体需求选择合适的实现。
常用方法
put(E e)
:将元素添加到队列中,如果队列已满,则阻塞直到有空间可用。take()
:从队列中移除并返回头部元素,如果队列为空,则阻塞直到有元素可用。offer(E e, long timeout, TimeUnit unit)
:尝试在指定时间内将元素加入队列,如果队列满则等待指定时间,超时后返回false
。poll(long timeout, TimeUnit unit)
:尝试在指定时间内从队列中取出一个元素,如果队列为空则等待指定时间,超时后返回null
。drainTo(Collection<? super E> c)
:尽可能多地将队列中的元素转移到指定集合中,返回转移的元素数量。
适用场景
- 生产者-消费者模式:一个或多个生产者线程负责生产数据并将其放入队列,同时一个或多个消费者线程从队列中取出数据进行处理。
- 任务队列:在实现线程池时,可以使用
BlockingQueue
来存放待处理的任务,线程池中的工作线程从队列中取出任务并执行。 - 流控和缓冲:在需要控制数据流速率或缓冲区大小的场景中,使用有界
BlockingQueue
可以有效防止资源耗尽。
实现原理简析
以ArrayBlockingQueue
为例,它内部维护了一个定长数组作为存储容器,并使用两个ReentrantLock(分别用于入队和出队操作)和两个Condition(用于通知等待的线程)来实现线程的阻塞和唤醒逻辑。当队列为空或满时,试图执行相应操作的线程会被挂起,等待条件满足后再被唤醒。
使用场景
Java的BlockingQueue
接口及其实现类在多线程编程中扮演着重要角色,特别是在需要协调多个线程之间数据传递的场景中。以下是几个典型的使用场景,展示了BlockingQueue
的实用性和灵活性:
1. 生产者-消费者模式
这是BlockingQueue
最经典的使用场景。一个或多个生产者线程负责生成数据并将其放入队列,同时一个或多个消费者线程从队列中取出数据进行处理。BlockingQueue
的阻塞特性完美地解决了生产速度与消费速度不一致的问题,当队列为空时,消费者线程会阻塞等待,反之生产者线程在队列满时也会阻塞,从而实现自动的流量控制和线程同步。
2. 线程池任务调度
在实现自定义线程池或使用Executors
框架时,BlockingQueue
常被用作任务队列。工作线程从队列中获取任务执行,而新的任务则被提交到队列中。通过选择不同类型的BlockingQueue
(如无界队列LinkedBlockingQueue
或有界队列ArrayBlockingQueue
),可以控制任务的接纳策略和线程池的响应性。有界队列尤其适用于限制线程池的最大工作负载,防止资源耗尽。
3. 消息队列与事件处理
在需要异步处理消息或事件的系统中,BlockingQueue
可以充当简单的消息队列。事件生产者将事件放入队列,而事件处理器从队列中取出并处理这些事件。这种模式有利于解耦生产者和消费者,提高系统的可扩展性和响应性。
4. 批量处理
在需要批量处理数据的场景下,可以利用BlockingQueue
来累积一定数量的数据项,然后再一次性处理。例如,数据库批量插入操作,可以先将数据放入队列,当队列达到一定大小后,再一次性执行插入操作,从而减少数据库交互次数,提高效率。
5. 限流与平滑处理
在高并发系统中,BlockingQueue
可以用作一种简单的限流手段。通过设置队列的大小上限,可以限制系统在短时间内处理请求的数量,避免瞬时压力过大导致系统崩溃。同时,它还能帮助平滑处理请求,即使面对请求波峰,也能保持服务的稳定性。
6. 同步点
在需要多个线程同步的场景中,BlockingQueue
可以作为一个同步点。例如,一个线程需要等待多个线程完成各自的任务后才能继续执行,可以使用BlockingQueue
作为信号机制,每个任务完成时向队列中放入一个标记,主线程等待特定数量的标记后继续执行。
实际开发中的使用详情
在实际开发中,使用BlockingQueue
能够显著提升代码的并发性能和线程安全,以下是具体使用详情的展开讲解,包括选择合适的队列类型、编写示例代码、异常处理、以及一些最佳实践。
1. 选择合适的队列类型
Java标准库提供了多种BlockingQueue
的实现,选择合适的类型对于优化系统性能至关重要:
- ArrayBlockingQueue:基于数组的有界阻塞队列,适合于固定大小的队列,且对内存使用有严格要求的场景。
- LinkedBlockingQueue:基于链表的队列,可以是有界也可以是无界的(默认无界)。无界队列在生产者速度远大于消费者速度时可能造成内存溢出。
- PriorityBlockingQueue:一个无界优先级队列,元素按照自然排序或提供的比较器排序。适用于需要按优先级处理任务的场景。
- DelayQueue:一个无界延迟队列,元素只有在延迟期满后才能被取出。适用于定时任务调度。
- SynchronousQueue:不存储元素的阻塞队列,每个插入操作必须等待另一个线程的对应移除操作。适合一对一的线程间传递。
2. 线程的创建与任务分配
- 使用
Thread
类或ExecutorService
框架来创建生产者和消费者线程。推荐使用ExecutorService
,因为它提供了更高级的线程管理功能,如线程池的复用、任务调度和线程生命周期管理。
3. 数据生产和消费
- 生产者:使用
put()
或offer()
方法向队列中添加元素。put()
在队列满时会阻塞直到有空间,而offer()
可以在指定时间内尝试插入,超时则返回false
。 - 消费者:使用
take()
或poll()
方法从队列中取出元素。take()
在队列空时会阻塞直到有元素,而poll()
可以在指定时间内尝试取出,超时则返回null
。
4. 编写示例代码
使用BlockingQueue
实现生产者-消费者模式
下面的示例展示了如何使用ArrayBlockingQueue
实现一个简单的生产者-消费者模型。在这个模型中,生产者不断地生成随机数并放入队列,而消费者从队列中取出数据并打印出来。这个过程展示了BlockingQueue
如何有效地管理线程间的协作和同步,确保数据的生产和消费是线程安全的。
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;public class ProducerConsumerExample {public static void main(String[] args) {// 创建一个容量为10的阻塞队列BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);// 创建生产者线程Thread producer = new Thread(new Producer(queue));// 创建消费者线程Thread consumer = new Thread(new Consumer(queue));// 启动线程producer.start();consumer.start();}static class Producer implements Runnable {private final BlockingQueue<Integer> queue;public Producer(BlockingQueue<Integer> queue) {this.queue = queue;}@Overridepublic void run() {try {Random random = new Random();while (true) {// 生成随机数int number = random.nextInt(100);// 将数据放入队列,如果队列满,则阻塞等待queue.put(number);System.out.println("Produced: " + number);Thread.sleep(1000); // 模拟生产间隔}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}static class Consumer implements Runnable {private final BlockingQueue<Integer> queue;public Consumer(BlockingQueue<Integer> queue) {this.queue = queue;}@Overridepublic void run() {try {while (true) {// 从队列中取出数据,如果队列空,则阻塞等待int number = queue.take();System.out.println("Consumed: " + number);Thread.sleep(1500); // 模拟消费间隔}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}
}
解释说明
- 该示例中,
Producer
和Consumer
类分别实现了Runnable
接口,代表生产者和消费者线程。BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
创建了一个容量为10的ArrayBlockingQueue
,即最多可以存储10个元素。- 生产者线程通过
queue.put(number);
方法尝试将生成的随机数放入队列,如果队列已满,该方法将阻塞,直到有空间可用。- 消费者线程通过
queue.take();
方法从队列中取出一个元素,如果队列为空,该方法将阻塞,直到有元素可取。Thread.sleep(1000)
和Thread.sleep(1500)
分别模拟了生产者和消费者的处理时间,让示例更加真实。- 通过
InterruptedException
的捕获和处理,确保了线程在被中断时能够正确清理资源。这个例子展示了如何利用
BlockingQueue
实现线程间的高效协作,以及如何处理线程的同步和异常情况。
5. 异常处理
在使用BlockingQueue
时,需要注意处理InterruptedException
。通常,当线程在等待队列的put
或take
操作时被中断,会抛出此异常。最佳做法是捕获该异常,并重新设置中断标志,以便上层调用者能感知到中断事件。
- InterruptedException:当阻塞操作被中断时抛出,处理时应重新设置中断标志,例如通过
Thread.currentThread().interrupt();
。- 其他异常:如
NullPointerException
,确保放入队列的元素非空,避免抛出异常。
6. 最佳实践
- 合理设置队列容量:对于有界队列,合理设置队列大小可以有效防止内存溢出和过度缓冲,同时影响系统的吞吐量和响应时间。
- 考虑公平性:某些队列实现允许设置是否公平访问,公平策略可以减少“饥饿”现象,但可能降低整体吞吐量。
- 资源清理:确保在不再使用队列时正确关闭相关资源,尤其是在使用带资源的队列时(尽管大多数
BlockingQueue
实现不需要显式关闭)。 - 监控与调试:利用Java的并发工具和日志记录,监控队列的状态(如队列长度、等待线程数等),有助于发现和解决问题。
7. 性能优化与监控
- 容量选择:合理设置队列容量,避免队列频繁满或空导致的线程频繁阻塞和唤醒。
- 公平性设置:对于高竞争的场景,考虑使用公平策略减少线程饥饿现象,但需注意公平策略可能降低吞吐量。
- 监控:使用JMX监控队列状态,比如队列长度、线程等待情况,有助于及时发现并解决问题。
注意事项
在使用Java的BlockingQueue
时,为了确保代码的健壮性、性能和正确性,有几个关键的注意事项需要遵循:
1. 容量选择
- 有界队列与无界队列:选择有界还是无界队列需根据实际需求。有界队列能够防止资源耗尽,限制系统负载,但需要合理设置队列大小,过大可能导致响应时间增加,过小则可能频繁阻塞生产者线程。无界队列则需谨慎使用,因为它可能因生产速度过快而耗尽系统资源。
2. 线程中断
- 处理中断:在调用
put()
、take()
等可能阻塞的方法时,应妥善处理InterruptedException
。当线程被中断时,应尊重中断状态,通常通过重新设置中断标志(Thread.currentThread().interrupt();
)并优雅地退出循环或方法。
3. 公平性设置
- 公平性策略:某些
BlockingQueue
(如ArrayBlockingQueue
)允许设置公平性,公平策略可以减少饥饿现象,保证等待时间最长的线程优先获得服务,但这可能会牺牲一些吞吐量。根据应用场景权衡是否开启公平性。
4. 资源管理
- 内存管理:特别是对于存储大型对象或大量对象的队列,要注意队列的内存占用,避免内存泄漏或内存溢出。
- 关闭和清理:虽然
BlockingQueue
自身不需要显式关闭,但与其相关的资源(如在队列中传递的数据库连接、文件流等)需要妥善关闭。
5. 同步与并发控制
- 单一职责:尽量确保每个线程只做生产或消费一件事,避免在同一个线程中同时执行生产与消费操作,这有助于提高代码的清晰度和可维护性。
- 避免外部同步:
BlockingQueue
内部已经实现了必要的同步,通常情况下不需要外部额外的锁。过度同步可能导致死锁或其他并发问题。
6. 性能监控
- 监控队列状态:使用JMX或其他监控工具定期检查队列的长度、等待线程数等指标,可以帮助及时发现和解决性能瓶颈。
- 负载平衡:在多消费者或多生产者的场景下,注意负载均衡,避免某些线程过载或空闲。
7. 测试与调试
- 单元测试:编写充分的单元测试,模拟不同的生产者-消费者场景,包括高并发、边界条件(如队列满/空)等,确保代码的健壮性。
- 日志记录:适当添加日志记录,尤其是在关键的生产、消费和阻塞点,有助于问题定位和性能分析。
遵循上述注意事项,可以确保在实际项目中高效、安全地使用
BlockingQueue
,充分发挥其在多线程编程中的优势。
优缺点
优点
-
线程安全与同步:
BlockingQueue
是线程安全的,它自动管理了多线程环境下的同步问题,开发者无需编写额外的同步代码,大大降低了编程复杂度和出错概率。 -
生产者-消费者模式的天然实现:它完美地支持了生产者-消费者模式,通过阻塞和唤醒机制,实现了生产者和消费者线程之间的高效协调,无需担心竞态条件。
-
灵活的阻塞策略:提供了阻塞和非阻塞两种操作方式(如
put()
与offer()
、take()
与poll()
),可以根据具体需求选择最适合的策略。 -
流量控制:尤其是有界队列,可以作为天然的流量控制工具,限制系统处理任务的速度,防止资源耗尽或过载。
-
提高系统吞吐量:通过减少线程间的直接同步,减少上下文切换,提高了并发处理的效率和系统整体吞吐量。
-
丰富的实现选择:Java并发包提供了多种
BlockingQueue
的实现,如ArrayBlockingQueue
、LinkedBlockingQueue
、PriorityBlockingQueue
等,满足不同场景下的需求。
缺点
-
潜在的阻塞风险:特别是在使用无界队列或不恰当的队列大小时,如果生产速度远大于消费速度,可能导致队列无限增长,最终耗尽系统资源。
-
性能开销:虽然自动的同步和阻塞机制简化了编程,但是相比直接的线程间传递,使用
BlockingQueue
会有一定的性能开销,尤其是在高并发、低延迟的应用中。 -
调试困难:由于线程的阻塞和唤醒机制,当程序出现死锁或性能问题时,调试和问题定位可能较为复杂。
-
公平性与吞吐量的权衡:启用公平策略可以减少线程饥饿,但可能牺牲系统吞吐量。选择合适的队列和策略需要基于对应用特性的深入理解。
-
功能局限性:尽管
BlockingQueue
非常强大,但它主要是为了解决线程间的数据传递问题,对于更复杂的并发控制或数据处理逻辑,可能需要配合其他并发工具一起使用。
BlockingQueue
是Java并发编程中处理线程间通信的强大工具,但在使用时需根据具体场景合理选择队列类型,正确处理阻塞和中断,以避免潜在的性能问题和资源耗尽风险。
可能遇到的问题及解决方案
在使用上述Java生产者-消费者模型时,可能会遇到一些常见问题,下面我将详细展开这些问题及其解决方案:
1. 死锁
问题描述:如果生产者和消费者在不恰当的时机互相等待对方,可能导致整个程序陷入死锁状态。
解决方案:
- 确保
put
和take
操作的使用不会导致循环等待。在这个示例中,因为仅使用了BlockingQueue
进行同步,且没有其他复杂的锁依赖,所以直接使用标准库的阻塞队列通常能避免死锁问题。 - 避免在持有锁的情况下调用可能阻塞的方法,除非你确切知道这样做不会引发死锁。
2. 资源泄露
问题描述:未正确处理线程中断可能导致资源无法释放,如线程池未关闭等。
解决方案:
- 在捕获到
InterruptedException
时,通过调用Thread.currentThread().interrupt();
重新设置中断标志,确保上层调用者能感知到中断事件。 - 使用完毕后,如果适用,确保关闭相关的资源,比如关闭文件流、数据库连接等。
3. 饿死问题
问题描述:如果生产者速度远大于消费者,或者反之,可能导致一方始终无法获取到资源,出现“饿死”现象。
解决方案:
- 采用动态调整策略,比如根据队列的当前长度调整生产或消费的速度。
- 可以考虑使用优先级队列(
PriorityBlockingQueue
),虽然在这个场景中可能不直接适用,但在某些情况下能帮助解决特定类型的饿死问题。
4. 性能瓶颈
问题描述:高并发环境下,队列成为瓶颈,影响整体吞吐量。
解决方案:
- 考虑使用更高效的并发队列,如
ConcurrentLinkedQueue
(非阻塞),适合高并发但不严格要求顺序的场景。 - 调整队列容量,根据实际需求和系统资源合理设定。
- 分析并优化生产者和消费者的逻辑,减少不必要的计算和等待时间。
5. 数据一致性问题
问题描述:在多线程环境下,除了队列本身的同步问题外,生产者或消费者内部的状态变更也可能导致一致性问题。
解决方案:
- 使用同步机制(如
synchronized
块、ReentrantLock
)保护共享资源的访问。 - 应用原子操作(
AtomicInteger
等)减少对锁的依赖,提高并发性能。 - 确保对队列的操作是原子的,并且在必要时使用更高级的并发工具类,如
CountDownLatch
、CyclicBarrier
进行更复杂的同步控制。
生产者-消费者模式在实现过程中需要细心考虑线程间的协调与同步,以及潜在的性能和可靠性问题。通过合理的队列选择、适当的同步机制、以及对系统负载的合理评估和调整,可以有效避免上述问题,构建出稳定高效的并发系统。
相关文章:

JUC下的BlockingQueue详解
BlockingQueue是Java并发包(java.util.concurrent)中提供的一个接口,它扩展了Queue接口,增加了阻塞功能。这意味着当队列满时尝试入队操作,或者队列空时尝试出队操作,线程会进入等待状态,直到队列状态允许操作继续。这…...

ChatGPT理论分析
ChatGPT "ChatGPT"是一个基于GPT(Generative Pre-trained Transformer)架构的对话系统。GPT 是一个由OpenAI 开发的自然语言处理(NLP)模型,它使用深度学习来生成文本。以下是对ChatGPT进行理论分析的几个主…...

算法提高之魔板
算法提高之魔板 核心思想:最短路模型 将所有状态存入队列 更新步数 同时记录前驱状态 #include <iostream>#include <cstring>#include <algorithm>#include <unordered_map>#include <queue>using namespace std;string start&qu…...

服务器内存占用不足会怎么样,解决方案
在当今数据驱动的时代,服务器对于我们的工作和生活起着举足轻重的作用。而在众多影响服务器性能的关键因素当中,内存扮演着极其重要的角色。 服务器内存,也称RAM(Random Access Memory),是服务器核心硬件部…...

elasticsearch文档读写原理大致分析一下
文档写简介 客户端通过hash选择一个node发送请求,专业术语叫做协调节点 协调节点会对document进行路由,将请求转发给对应的primary shard primary shard在处理完数据后,会将document 同步到所有replica shard 协调节点将处理结果返回给…...

1 开发环境
开发环境(platformio python arduino框架)的搭建可以参考b站upESP32超详细教程-使用VSCode(基于Arduino框架)哔哩哔哩bilibili 这里推荐离线安装esp32库文件,要不然要等很久(b站教程很多) 搭…...

云视频,也称为视频云服务,是一种基于云计算技术理念的视频流媒体服务
云视频,也称为视频云服务,是一种基于云计算技术理念的视频流媒体服务。它基于云计算商业模式,为视频网络平台服务提供强大的支持。在云平台上,所有的视频供应商、代理商、策划服务商、制作商、行业协会、管理机构、行业媒体和法律…...

[Vision Board创客营]--使用openmv识别阿尼亚
文章目录 [Vision Board创客营]使用openmv识别阿尼亚介绍环境搭建训练模型上传图片生成模型 使用结语 [Vision Board创客营]使用openmv识别阿尼亚 🚀🚀五一和女朋友去看了《间谍过家家 代号:白》,入坑二刺螈(QQ头像也换…...

【Linux:lesson1】的基本指令
🎁个人主页:我们的五年 🔍系列专栏:Linux课程学习 🌷追光的人,终会万丈光芒 🎉欢迎大家点赞👍评论📝收藏⭐文章 目录 🚗打开Xshell,登陆root…...

20240511日记
今天工作内容: 1.二期2号机EAP测试 2.二期开门机器暂停(停轴,停流水线电机),关闭门后继续功能测试 3.针点位偏移还需要调整,未进行大批量验证是否偏移(S3模板点位测试,两台机各焊…...

蓝桥杯成绩已出
蓝桥杯的成绩早就已经出来了,虽然没有十分惊艳 ,但是对于最终的结果我是心满意足的,感谢各位的陪伴,关于蓝桥杯的刷题笔记我已经坚持更新了49篇,但是现在即将会告别一段落,人生即将进入下一个规划。我们一起…...

.kat6.l6st6r勒索病毒数据怎么处理|数据解密恢复
导言: 在数字时代的洪流中,网络安全领域的新挑战层出不穷。近期,.kat6.l6st6r勒索病毒的出现再次打破了传统安全防护的界限。这种新型勒索病毒不仅具有高超的加密技术,更以其独特的传播方式和隐蔽性,给全球用户带来了…...

Spring Batch 是什么?主要用于什么场景?
Spring Batch是一个开源的、基于Spring框架的批量处理框架,它提供了一系列用于批量数据处理的工具和API。Spring Batch的主要目标是简化和标准化批量数据的处理过程,使得开发者可以更加专注于业务逻辑的实现,而不是批量处理的复杂性。 Sprin…...

SQL-慢查询的定位及优化
定位慢查询sql 启用慢查询日志: 确保MySQL实例已经启用了慢查询日志功能。可以通过以下命令查看是否启用: SHOW VARIABLES LIKE slow_query_log;如果未启用,可以通过以下命令启用: SET GLOBAL slow_query_log ON;配置慢查询日志&…...

练习题(2024/5/11)
1逆波兰表达式求值 给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。 请你计算该表达式。返回一个表示表达式值的整数。 注意: 有效的算符为 、-、* 和 / 。每个操作数(运算对象)都可以是一个整数或…...

linux系统服务器中常见故障及排查方法
目录 故障1:系统无响应 故障2:网络连接问题 故障3:文件系统错误 故障4:软件包依赖问题 故障5:用户权限问题 故障6:服务无法正常工作 故障7:磁盘空间不足 故障8:内存不足 故障…...

产品人生(5):从“敏捷开发”到“四化时间管理法”
人生如产品,产品映人生,借鉴产品思维,快速提升软技能! 在互联网的敏捷开发实践中,经常会用到“流程化、模板化、清单化、不断优化”的思想来提升开发的效率和产品质量,并确保团队能够快速响应市场变化。大…...

超级好看的html网站维护源码
源码介绍 好看的html网站维护源码,源码由HTMLCSSJS组成,记事本打开源码文件可以进行内容文字之类的修改,双击html文件可以本地运行效果,也可以上传到服务器里面, 源码截图 源码下载 好看的html网站维护源码...

从零开始搭建Springboot项目脚手架2:配置文件、返回值、日志等
1、多个环境与配置文件 2、统一返回值 返回值包括两种场景:正常controller的返回、异常发生之后返回 正常controller的返回:通过在controller的默认返回Response实现 异常发生之后返回:通过全局异常处理统一捕获返回 首先创建类StatusCode…...

Java web第五次作业
1.在idea中配置好数据源 2、视频案例中只给出了查询所有结果的示例,请自己完成添加、删除、修改操作的代码。以下供参 考。 Delete("delete from emp where id#{id}") public void delete(Integer id); 测试代码 Test public void testDelete(){ empMa…...

Unity使用ToggleGroup对多个Toggle进行管理时,初始化默认选项失效的问题
问题描述: 在unity脚本的OnEnable中用代码设置Toggle集合中的其中一个对象的ison时,发现并没有根据设置发生变化。但是该Toggle的OnValueChange却发生过变化。 如果使用协程等待0.01s,那么对应组件的ison的修改才能生效,但是逐帧分析的话会发…...

Retrofit同步请求直接返回目标对象
Rxjava方式: // 创建 Retrofit 实例 Retrofit retrofit new Retrofit.Builder().baseUrl("https://api.example.com/").addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build();// 创…...
Android GPU渲染屏幕绘制显示基础概念(1)
Android GPU渲染屏幕绘制显示基础概念(1) Android中的图像生产者OpenGL,Skia,Vulkan将绘制的数据存放在图像缓冲区中,Android中的图像消费SurfaceFlinger从图像缓冲区将数据取出,进行加工及合成。 Surface…...

Mac电脑设置hosts的方法
hosts文件是什么 hosts文件是一个系统文件,通过绑定域名与ip的关系,当本机访问该域名时 从这个文件中如果找到了对应域名,则转发到对应ip;如果没有找到对应域名,则走默认的DNS公网解析。 好处: 加速访问…...

数据分析——大数据伦理风险分析
大数据伦理风险分析 前言一、大数据伦理二、大数据技术伦理风险算法安全性、可信赖性及稳定性风险及其应对算法风险的表现算法风险的危害算法风险的应对 算法的可解释性风险及其应对算法可解释性风险的内容算法可解释性风险的损害算法可解释性风险的应对 算法的决策不可预见性风…...

漫谈AI时代的手机
以chatGPT 为代表的大语言的横空出世使人们感受到AI 时代的到来,大语言模型技术的最大特点是机器开始”懂人话“,”说人话“了。如同任何一个革命性工具的出现一样,它必将改变人类生活和工作。 在这里。我谈谈AI时代的手机。 语音通信的历史…...

fatal error: ros/ros.h: 没有那个文件或目录
解决方法: 在出错的文件的包下的CMakeLists.txt文件里,加上 find_package(catkin REQUIRED COMPONENTSroscpp )include_directories(include ${catkin_INCLUDE_DIRS} )【ROS-解决问题】 fatal error: ros/ros.h: 没有那个文件或目录-CSDN博客...

苍穹外卖Day06笔记(复习了jwt的加密解密和传递)
疯玩了一个月,效率好低,今天开始捡起来苍穹外卖~ 1. 为什么不需要单独引入HttpClient的dependency? 因为我们在sky-common的pom.xml中已经引入了aliyun-sdk-oss的依赖,而这个依赖低层就引入了httpclinet的依赖,根据依…...

【ARM 嵌入式 C 字符串系列 23.9 -- strcmp 与 strncmp 在使用上的区别以及注意事项】
请阅读【嵌入式开发学习必备专栏】 文章目录 strcmp 与 strncmp 使用介绍strcmpstrncmp使用建议 strcmp 与 strncmp 使用介绍 strcmp 和 strncmp 都是 C 语言标准库中用于比较两个字符串的函数,它们定义在 <string.h> 头文件中。这两个函数在功能上相似&…...

行列视(RCV):企业数据处理的革新工具
在当前数据驱动的商业生态系统中,行列视(RCV)系统以其创新的企业数据处理功能,不断地为各行各业的企业带来变革。行列视系统能够处理大规模数据集,支持多达400种Excel函数,使得数据处理不仅限于基本的表格操…...