mirror of
https://github.com/Snailclimb/JavaGuide
synced 2025-06-16 18:10:13 +08:00
[docs update]补充几个工作流引擎开源项目
This commit is contained in:
parent
c800fc05bf
commit
7dde6a2e51
@ -18,90 +18,33 @@ head:
|
||||
|
||||
### ThreadLocal 有什么用?
|
||||
|
||||
通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。**如果想实现每一个线程都有自己的专属本地变量该如何解决呢?**
|
||||
通常情况下,我们创建的变量可以被任何一个线程访问和修改。这在多线程环境中可能导致数据竞争和线程安全问题。那么,**如果想让每个线程都有自己的专属本地变量,该如何实现呢?**
|
||||
|
||||
JDK 中自带的`ThreadLocal`类正是为了解决这样的问题。 **`ThreadLocal`类主要解决的就是让每个线程绑定自己的值,可以将`ThreadLocal`类形象的比喻成存放数据的盒子,盒子中可以存储每个线程的私有数据。**
|
||||
JDK 中提供的 `ThreadLocal` 类正是为了解决这个问题。**`ThreadLocal` 类允许每个线程绑定自己的值**,可以将其形象地比喻为一个“存放数据的盒子”。每个线程都有自己独立的盒子,用于存储私有数据,确保不同线程之间的数据互不干扰。
|
||||
|
||||
如果你创建了一个`ThreadLocal`变量,那么访问这个变量的每个线程都会有这个变量的本地副本,这也是`ThreadLocal`变量名的由来。他们可以使用 `get()` 和 `set()` 方法来获取默认值或将其值更改为当前线程所存的副本的值,从而避免了线程安全问题。
|
||||
当你创建一个 `ThreadLocal` 变量时,每个访问该变量的线程都会拥有一个独立的副本。这也是 `ThreadLocal` 名称的由来。线程可以通过 `get()` 方法获取自己线程的本地副本,或通过 `set()` 方法修改该副本的值,从而避免了线程安全问题。
|
||||
|
||||
再举个简单的例子:两个人去宝屋收集宝物,这两个共用一个袋子的话肯定会产生争执,但是给他们两个人每个人分配一个袋子的话就不会出现这样的问题。如果把这两个人比作线程的话,那么 ThreadLocal 就是用来避免这两个线程竞争的。
|
||||
|
||||
### 如何使用 ThreadLocal?
|
||||
|
||||
相信看了上面的解释,大家已经搞懂 `ThreadLocal` 类是个什么东西了。下面简单演示一下如何在项目中实际使用 `ThreadLocal` 。
|
||||
举个简单的例子:假设有两个人去宝屋收集宝物。如果他们共用一个袋子,必然会产生争执;但如果每个人都有一个独立的袋子,就不会有这个问题。如果将这两个人比作线程,那么 `ThreadLocal` 就是用来避免这两个线程竞争同一个资源的方法。
|
||||
|
||||
```java
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Random;
|
||||
public class ThreadLocalExample {
|
||||
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
|
||||
|
||||
public class ThreadLocalExample implements Runnable{
|
||||
|
||||
// SimpleDateFormat 不是线程安全的,所以每个线程都要有自己独立的副本
|
||||
private static final ThreadLocal<SimpleDateFormat> formatter = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd HHmm"));
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
ThreadLocalExample obj = new ThreadLocalExample();
|
||||
for(int i=0 ; i<10; i++){
|
||||
Thread t = new Thread(obj, ""+i);
|
||||
Thread.sleep(new Random().nextInt(1000));
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("Thread Name= "+Thread.currentThread().getName()+" default Formatter = "+formatter.get().toPattern());
|
||||
try {
|
||||
Thread.sleep(new Random().nextInt(1000));
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//formatter pattern is changed here by thread, but it won't reflect to other threads
|
||||
formatter.set(new SimpleDateFormat());
|
||||
|
||||
System.out.println("Thread Name= "+Thread.currentThread().getName()+" formatter = "+formatter.get().toPattern());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
输出结果 :
|
||||
|
||||
```plain
|
||||
Thread Name= 0 default Formatter = yyyyMMdd HHmm
|
||||
Thread Name= 0 formatter = yy-M-d ah:mm
|
||||
Thread Name= 1 default Formatter = yyyyMMdd HHmm
|
||||
Thread Name= 2 default Formatter = yyyyMMdd HHmm
|
||||
Thread Name= 1 formatter = yy-M-d ah:mm
|
||||
Thread Name= 3 default Formatter = yyyyMMdd HHmm
|
||||
Thread Name= 2 formatter = yy-M-d ah:mm
|
||||
Thread Name= 4 default Formatter = yyyyMMdd HHmm
|
||||
Thread Name= 3 formatter = yy-M-d ah:mm
|
||||
Thread Name= 4 formatter = yy-M-d ah:mm
|
||||
Thread Name= 5 default Formatter = yyyyMMdd HHmm
|
||||
Thread Name= 5 formatter = yy-M-d ah:mm
|
||||
Thread Name= 6 default Formatter = yyyyMMdd HHmm
|
||||
Thread Name= 6 formatter = yy-M-d ah:mm
|
||||
Thread Name= 7 default Formatter = yyyyMMdd HHmm
|
||||
Thread Name= 7 formatter = yy-M-d ah:mm
|
||||
Thread Name= 8 default Formatter = yyyyMMdd HHmm
|
||||
Thread Name= 9 default Formatter = yyyyMMdd HHmm
|
||||
Thread Name= 8 formatter = yy-M-d ah:mm
|
||||
Thread Name= 9 formatter = yy-M-d ah:mm
|
||||
```
|
||||
|
||||
从输出中可以看出,虽然 `Thread-0` 已经改变了 `formatter` 的值,但 `Thread-1` 默认格式化值与初始化值相同,其他线程也一样。
|
||||
|
||||
上面用于创建 `ThreadLocal` 变量的那段代码用到了 Java8 的知识,它等于下面这段代码,如果你写了下面这段代码的话,IDEA 会提示你转换为 Java8 的格式(IDEA 真的不错!)。因为 ThreadLocal 类在 Java 8 中扩展,使用一个新的方法`withInitial()`,将 Supplier 功能接口作为参数。
|
||||
|
||||
```java
|
||||
private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue(){
|
||||
return new SimpleDateFormat("yyyyMMdd HHmm");
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
Runnable task = () -> {
|
||||
int value = threadLocal.get();
|
||||
value += 1;
|
||||
threadLocal.set(value);
|
||||
System.out.println(Thread.currentThread().getName() + " Value: " + threadLocal.get());
|
||||
};
|
||||
|
||||
Thread thread1 = new Thread(task, "Thread-1");
|
||||
Thread thread2 = new Thread(task, "Thread-2");
|
||||
|
||||
thread1.start(); // 输出: Thread-1 Value: 1
|
||||
thread2.start(); // 输出: Thread-2 Value: 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ⭐️ThreadLocal 原理了解吗?
|
||||
|
@ -128,6 +128,13 @@ icon: "xitongsheji"
|
||||
- [EasyScheduler](https://github.com/analysys/EasyScheduler "EasyScheduler") (已经更名为 DolphinScheduler,已经成为 Apache 孵化器项目):分布式易扩展的可视化工作流任务调度平台,主要解决“复杂任务依赖但无法直接监控任务健康状态”的问题。
|
||||
- [PowerJob](https://gitee.com/KFCFans/PowerJob):新一代分布式任务调度与计算框架,支持 CRON、API、固定频率、固定延迟等调度策略,提供工作流来编排任务解决依赖关系,使用简单,功能强大,文档齐全,欢迎各位接入使用!<http://www.powerjob.tech/> 。
|
||||
|
||||
## 工作流
|
||||
|
||||
1. [Flowable](https://github.com/flowable/flowable-engine) :Activiti5 的一个分支发展而来,功能丰富,在 Activiti 的基础上,引入了更多高级功能,如更强大的 CMMN(案例管理模型与符号)、DMN(决策模型与符号)支持,以及更灵活的集成选项。
|
||||
2. [Activiti](https://github.com/Activiti/Activiti):功能扩展相对保守,适合需要稳定 BPMN 2.0 工作流引擎的传统企业应用。
|
||||
3. [Warm-Flow](https://gitee.com/dromara/warm-flow):国产开源工作流引擎,其特点简洁轻量但又不简单,五脏俱全,组件独立,可扩展。
|
||||
4. [FlowLong](https://gitee.com/aizuda/flowlong):国产开源工作流引擎,专门中国特色流程审批打造。
|
||||
|
||||
## 分布式
|
||||
|
||||
### API 网关
|
||||
|
Loading…
x
Reference in New Issue
Block a user