由于业务接触到了多线程,处理GPS数据,包括通过GPS去计算作业时长这些业务,看了大佬留下的代码晦涩难懂,于是先从简单的用起来,由于业务复杂,大佬采用多线程和EventBus等等 ,本文只先说多线程
前提知识
Deque
Deque (发音为"deck") 是双端队列的缩写,它是一种允许在两端(头部和尾部)进行插入和删除操作的线性数据结构。
- 双端访问: 这是 deque 最重要的特征。你可以从 deque 的两端高效地添加或移除元素。
- 高效的插入和删除: 在 deque 的两端进行插入和删除操作通常具有 O(1) 的时间复杂度,这使得它非常适合需要频繁在两端进行操作的应用场景。
- 动态大小: 大多数 deque 实现都能够动态调整大小,以适应不断变化的数据量。
- 非线性访问: 虽然可以在两端快速访问,但访问 deque 中间位置的元素则需要线性时间,效率较低。
ReentrantLock
lock
Lock 的接口定义
java
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
通过这些接口函数的定义,我们可以发现 Lock具备一些灵活性,尤其是在它的以下几个特性方面:
-
支持获取锁超时机制;
-
支持非阻塞方式获取锁;
-
支持可中断方式获取锁。
在JDK包中,Lock 实现类的代表莫过于是 ReentrantLock
Condition
Condition 是一种高级别的线程协调工具,它能够让线程在特定的条件下等待或通知其他线程。Condition 可以与 Lock 一起使用,来实现更复杂的线程协调。
以下是 Condition 的常用方法:
await()
:当前线程进入等待状态,直到其他线程调用signal()
或signalAll()
方法才会被唤醒。signal()
:唤醒一个正在等待的线程。signalAll()
:唤醒所有正在等待的线程。
ReentrantLock
ReentrantLock是一种基于AQS(Abstract Queued Synchronizer)框架的应用实现,是JDK中一种线程并发访问的同步手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全。
思路
采用经典的设计模型------生产者消费者模型
首先得有得有一个任务等待队列,把任务都丢进去
然后生产者放入任务
消费者消费任务
由于我是自己写一个简易的,就有main负责put任务到任务等待队列
由线程池去task任务
任务等待队列BlockingQueue
java
@Slf4j(topic = "c.BlockingQueue")
public class BlockingQueue<T> {
// 容量
private int capcity;
// 双端任务队列容器
private Deque<T> deque = new ArrayDeque<>();
// 重入锁
private ReentrantLock lock = new ReentrantLock();
// 生产者条件变量
private Condition fullWaitSet = lock.newCondition();
// 生产者条件变量
private Condition emptyWaitSet = lock.newCondition();
public BlockingQueue(int capcity) {
this.capcity = capcity;
}
// 阻塞的方式添加任务
public void put(T task) {
lock.lock();
try {
// 通过while的方式
while (deque.size() >= capcity) {
log.debug("wait to add queue");
try {
fullWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
deque.offer(task);
log.debug("task add successfully");
emptyWaitSet.signal();
} finally {
lock.unlock();
}
}
// 阻塞获取任务
public T take() {
lock.lock();
try {
// 通过while的方式
while (deque.isEmpty()) {
try {
log.debug("wait to take task");
emptyWaitSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
fullWaitSet.signal();
T task = deque.poll();
log.debug("take task successfully");
// 从队列中获取元素
return task;
} finally {
lock.unlock();
}
}
}
线程池消费端实现
- 定义执行器接口
php
php 代码解读复制代码/**
* <p>定义一个执行器的接口:</p>
*
* @author: cxw (332059317@qq.com)
* @date: 2022/10/18 12:31
* @version: 1.0.0
*/
public interface Executor {
/**
* 提交任务执行
* @param task 任务
*/
void execute(Runnable task);
}
- 定义线程池类实现该接口
java
java 代码解读复制代码@Slf4j(topic = "c.ThreadPool")
public class ThreadPool implements Executor {
/**
* 任务队列
*/
private BlockingQueue<Runnable> taskQueue;
/**
* 核心工作线程数
*/
private int coreSize;
/**
* 工作线程集合
*/
private Set<Worker> workers = new HashSet<>();
/**
* 创建线程池
* @param coreSize 工作线程数量
* @param capcity 阻塞队列容量
*/
public ThreadPool(int coreSize, int capcity) {
this.coreSize = coreSize;
this.taskQueue = new BlockingQueue<>(capcity);
}
/**
* 提交任务执行
*/
@Override
public void execute(Runnable task) {
synchronized (workers) {
// 如果工作线程数小于阈值,直接开始任务执行
if(workers.size() < coreSize) {
Worker worker = new Worker(task);
workers.add(worker);
worker.start();
} else {
// 如果超过了阈值,加入到队列中
taskQueue.put(task);
}
}
}
/**
* 工作线程,对执行的任务做了一层包装处理
*/
class Worker extends Thread {
private Runnable task;
public Worker(Runnable task) {
this.task = task;
}
@Override
public void run() {
// 如果任务不为空,或者可以从队列中获取任务
while (task != null || (task = taskQueue.take()) != null) {
try {
task.run();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 执行完后,设置任务为空
task = null;
}
}
// 移除工作线程
synchronized (workers){
log.debug("remove worker successfully");
workers.remove(this);
}
}
}
}
演示
java
@Slf4j(topic = "c.ThreadPool")
public class Main {
public static void main(String[] args) throws InterruptedException {
System.out.println("Hello world!");
Executor executor = new ThreadPool(2, 4);
// 提交任务
for (int i = 0; i < 6; i++) {
final int j = i;
executor.execute(() -> {
try {
Thread.sleep(10);
log.info("run task {}", j);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread.sleep(10);
}
Thread.sleep(10000);
}
}
由于只是简单demo,没有考虑超过丢弃等等问题,只是作为入门