Java 关于弹簧J的一种预防方法——螺纹安全
我很好奇SpringJParepository方法是否是线程安全的,然后我阅读了stackflow文章()。从那里,我了解到存储库方法是线程安全的,然后我制作了一个POC来测试线程安全性。我让一个存储库说FormRepository来为“form”实体执行CRUD操作,这是对JpaRepository的扩展。从DAO中,我只需调用100个线程来创建表单对象,手动设置其id,然后保存“表单”对象 以下是参考代码:-Java 关于弹簧J的一种预防方法——螺纹安全,java,multithreading,hibernate,spring-data,spring-data-jpa,Java,Multithreading,Hibernate,Spring Data,Spring Data Jpa,我很好奇SpringJParepository方法是否是线程安全的,然后我阅读了stackflow文章()。从那里,我了解到存储库方法是线程安全的,然后我制作了一个POC来测试线程安全性。我让一个存储库说FormRepository来为“form”实体执行CRUD操作,这是对JpaRepository的扩展。从DAO中,我只需调用100个线程来创建表单对象,手动设置其id,然后保存“表单”对象 以下是参考代码:- @Repository public interface FormReposito
@Repository
public interface FormRepository extends JpaRepository<Tbldynamicform, Long> {
Tbldynamicform save(Tbldynamicform tblform);
@Query("SELECT max(tblform.formid) FROM Tbldynamicform tblform")
Optional<Integer> findMaxId();
}
......End of Repository above and start of DAO below...
@Component
public class DynamicFormDAO implements DynamicFormDAO {
@Inject
private FormRepository formRepository;
public void testThreadSafety() throws Exception {
List<Callable<Integer>> tasks = new ArrayList<>(100);
for (int i = 0; i < 100; i++) {
tasks.add(() -> {
try {
Tbldynamicform tbldynamicform = new Tbldynamicform();//Set all the required fields for form
if (tbldynamicform.getFormid() == null)
tbldynamicform.setFormid(findFormID());
Tbldynamicform form = formRepository.save(tbldynamicform);
return form.getFormid();
} catch (Exception e) {
e.printStackTrace();
}
return null;
});
}
ExecutorService executor = Executors.newFixedThreadPool(100);
executor.invokeAll(tasks);
}
private int findFormID() throws Exception {
Optional<Integer> id = formRepository.findMaxId();
if (id != null && id.isPresent() && id.get() != null) {
int generatedId = id.get().intValue();
return ++generatedId;
}
return 0;
}
}
@存储库
公共接口FormRepository扩展了JpaRepository{
Tbldynamicform保存(Tbldynamicform tblform);
@查询(“从Tbldynamicform tblform中选择最大值(tblform.formid))
可选的findMaxId();
}
……上面的存储库结束,下面的DAO开始。。。
@组成部分
公共类DynamicFormDAO实现DynamicFormDAO{
@注入
私有表单存储库表单存储库;
public void testThreadSafety()引发异常{
列表任务=新阵列列表(100);
对于(int i=0;i<100;i++){
任务。添加(()->{
试一试{
Tbldynamicform Tbldynamicform=新建Tbldynamicform();//设置表单的所有必需字段
if(tbldynamicform.getFormid()==null)
tbldynamicform.setFormid(findFormID());
Tbldynamicform form=formRepository.save(Tbldynamicform);
返回form.getFormid();
}捕获(例外e){
e、 printStackTrace();
}
返回null;
});
}
ExecutorService executor=Executors.newFixedThreadPool(100);
执行人。调用所有(任务);
}
private int findFormID()引发异常{
可选id=formRepository.findMaxId();
if(id!=null&&id.isPresent()&&id.get()!=null){
int generatedId=id.get().intValue();
return++generateId;
}
返回0;
}
}
当我这样做时,我假设事情必须正常工作,因为表单存储库方法是线程安全的,但不知何故,我在日志中多次遇到sql dataintegrityviolationexception,导致插入多条记录失败。以下错误供参考:-
@Repository
public interface FormRepository extends JpaRepository<Tbldynamicform, Long> {
Tbldynamicform save(Tbldynamicform tblform);
@Query("SELECT max(tblform.formid) FROM Tbldynamicform tblform")
Optional<Integer> findMaxId();
}
......End of Repository above and start of DAO below...
@Component
public class DynamicFormDAO implements DynamicFormDAO {
@Inject
private FormRepository formRepository;
public void testThreadSafety() throws Exception {
List<Callable<Integer>> tasks = new ArrayList<>(100);
for (int i = 0; i < 100; i++) {
tasks.add(() -> {
try {
Tbldynamicform tbldynamicform = new Tbldynamicform();//Set all the required fields for form
if (tbldynamicform.getFormid() == null)
tbldynamicform.setFormid(findFormID());
Tbldynamicform form = formRepository.save(tbldynamicform);
return form.getFormid();
} catch (Exception e) {
e.printStackTrace();
}
return null;
});
}
ExecutorService executor = Executors.newFixedThreadPool(100);
executor.invokeAll(tasks);
}
private int findFormID() throws Exception {
Optional<Integer> id = formRepository.findMaxId();
if (id != null && id.isPresent() && id.get() != null) {
int generatedId = id.get().intValue();
return ++generatedId;
}
return 0;
}
}
org.springframework.dao.DataIntegrityViolationException:无法执行语句;SQL[n/a];约束[“PUBLIC.TBLDYNAMICFORM(FORMID)上的主键];SQL语句:
插入Tbldynamicform(clientid、copyfromexisting、creationdate、formdesc、formmode、formname、formtemplate、formtitle、procutype、status、formid)值(?、?、?、
这让我思考这是线程安全问题还是其他问题?据我所知,我在dao中创建的所有“tbldynamicform”对象都将保留在线程堆栈上。只有formRepository将位于堆存储上,如果formRepository方法是线程安全的,则必须在数据库中插入100条记录,而无需任何更改没问题
如果我执行setId并保存在synchronized块中,一切正常,但这不是我的意图,如果存储库方法是线程安全的,也不是必需的
专家们,有什么帮助吗?您的保存任务不是原子的-两个线程可能在其中一个保存新实体之前获取相同的最大id 然后,即使存储库的save方法是线程安全的,也不会有帮助
maxId是线程安全的,save是线程安全的,但是每个线程的runnable中的方法不是线程安全的。您的保存任务不是原子的-两个线程可能在其中一个保存新实体之前获取相同的最大id 然后,即使存储库的save方法是线程安全的,也不会有帮助
maxId是线程安全的,save是线程安全的,但是每个线程的runnable中的方法不是线程安全的。问题在于如何使用findFormID()检索最后一个ID,它在并发上下文中不起作用
如果两个线程同时请求一个ID怎么办?它们将检索相同的ID并创建两个具有相同ID的对象。这是您的问题
一些针对生成的ID的集成解决方案已经存在,除非您知道自己在做什么,否则不应该尝试实现自己的解决方案。问题在于如何使用findFormID()检索最后一个ID,它在并发上下文中不起作用
如果两个线程同时请求一个ID怎么办?它们将检索相同的ID并创建两个具有相同ID的对象。这是您的问题
一些用于生成ID的集成解决方案已经存在,除非您知道自己在做什么,否则您不应该尝试实现自己的解决方案。简单地说,是的,它是线程安全的,但您的数据库也是有状态的(显然),为了维护完整性,您可能需要锁定策略之类的东西(保持锁以使事情同步,或者使用乐观策略并在需要时重试)。正如有人在另一个答案中指出的那样,如果您只是使用不同的方法生成ID(检查SUID),您的代码将运行良好。简单地说,是的,它是线程安全的,但您的数据库也是有状态的(显然)为了保持完整性,您可能需要锁定策略(保持锁定以使事情同步,或者使用乐观策略并在需要时重试)。正如有人在另一个答案中指出的那样,如果您只是使用不同的方法生成ID(签出SUID),您的代码就可以正常工作“如果两个线程同时请求ID怎么办”因为findFormID是同步的,所以只有一个线程能够获取ID。但问题是save和setformid不是原子的。“如果两个线程同时请求ID怎么办?”“因为findFormID是同步的,所以只有一个线程能够获取id。但问题是save和setformid不是原子的。是的,我也这么认为,因为我在问题中也提到,如果我将save和findFormID放在syncronized块中,一切都会正常工作……是的,我