介绍
长话短说,您不应在并行流中使用事务。 这是因为并行流中的每个线程都有其自己的名称,因此它确实参与了事务。
Streams API旨在在某些准则下正常工作。 实际上,为了受益于并行性,不允许每个操作更改共享对象的状态(此类操作称为无副作用)。 如果您遵循此准则,并行流的内部实现将巧妙地拆分数据,将不同部分分配给独立线程,并合并最终结果。
这主要是由于实现事务的方式而产生的。 排序上,ThreadLocal变量用于标记参与事务的每个方法。 ThreadLocal变量无法将其变量保持在并行流中。 为了证明我已经创建了以下测试
import org.junit.Assert; import org.junit.Test; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.IntStream; public class ThreadNameTest { @Test public void threadLocalTest(){ ThreadContext.set( "MAIN" ); AtomicBoolean hasNamechanged = new AtomicBoolean( false ); IntStream.range( 0 , 10000000 ).boxed().parallel().forEach(n->{ if (! "MAIN" .equals(ThreadContext.get())){ hasNamechanged.set( true ); } }); Assert.assertTrue(hasNamechanged.get()); } private static class ThreadContext { private static ThreadLocal<String> val = ThreadLocal.withInitial(() -> "empty" ); public ThreadContext() { } public static String get() { return val.get(); } public static void set(String x) { ThreadContext.val.set(x); } } }
IntStream.range值越高,则测试成功的可能性就越大。
现在看看这个github项目https://github.com/diakogiannis/transactionplayground/
TransactionPlayground项目
我创建了一种以4种不同方式加载猫的服务
- 按顺序
curl -I -X GET http://localhost:8080/api/cats/all
- 顺序但抛出异常以创建回退标记
curl -I -X GET http://localhost:8080/api/cats/all-exception
- 在并行
curl -I -X GET http://localhost:8080/api/cats/all-parallel
- 并行但抛出异常以创建回退标记
curl -I -X GET http://localhost:8080/api/cats/all-parallel-exception
也有2个辅助呼叫
- 清理
curl -I -X DELETE http://localhost:8080/api/cats/
- 和一个实际查看猫的
curl -X GET http://localhost:8080/api/cats/
开始项目
请执行mvn clean package wildfly-swarm:run
正常订购
呼叫curl -I -X GET http://localhost:8080/api/cats/all
,然后curl -X GET http://localhost:8080/api/cats/
正常,无订单,又称平行
调用clean curl -I -X DELETE http://localhost:8080/api/cats/
调用curl -I -X GET http://localhost:8080/api/cats/all-parallel
然后curl -X GET http://localhost:8080/api/cats/
预期的结果是看到猫的清单。 无需订购。 这就是为什么并行流先到先服务并从列表中随机读取的原因。
正常,例外
调用clean curl -I -X DELETE http://localhost:8080/api/cats/
调用curl -I -X GET http://localhost:8080/api/cats/all-exception
然后curl -X GET http://localhost:8080/api/cats/
预期的结果是一个空列表。 这是因为该事务被标记为回滚,所以jdbc事务也被回滚,因此所有条目都没有按照ACID模型持久化到数据库中。
平行例外
调用clean curl -I -X DELETE http://localhost:8080/api/cats/
调用curl -I -X GET http://localhost:8080/api/cats/all-parallel-exception
然后curl -X GET http://localhost:8080/api/cats/
预期的结果不是一个空列表。 这是因为并行流中的每个线程都会打开自己的jdbc事务,并在完成后进行提交。 因此,每次执行此操作时,都会显示一些猫,直到出现异常并停止执行为止。 回滚仅在一个线程中进行。
翻译自: https://www.javacodegeeks.com/2019/09/should-parallel-streams-transaction-context.html