Synchronized和@Transactional 使用时,同步失效。

本文探讨了在Java中使用Synchronized关键字与Spring事务管理器(@Transactional)结合时出现的并发控制失效问题,并提出了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Synchronized 的用法问题。

8月份在做一个系统的用户提现功能,为控制并发,想过在用Synchronized method(){}或者method(){ Synchronized(this){ } }来控制,两个同样的记录去做并发提现测试,同步方法或者代码块并没有起作用。Synchronized加在@Transactional事务注解的方法时,同步并不起作用,可以新建不带用事务注解的同步方法,然后再调用method。

 

1:原接口实现同步实现方法

 

同步方法或者同步方法块都不能达到同步的作用。

 

@Service
public class SynchronizedService{

	@Transactional(readOnly = false,rollbackFor = SLException.class)
	public synchronized void synMethodOne() throws SLException{
	
	}
	
}

 

 

@Service
public class SynchronizedService{

	@Transactional(readOnly = false,rollbackFor = SLException.class)
	public void synMethodTwo() throws SLException{
		synchronized(this){
			
		}
	}
	
}

 

2:修改后的方法。

 

@Service
public class SynchronizedNoTransService {

	@Autowired
	SynchronizedService synchronizedService;
	
	public synchronized void synMethod() throws SLException{
		synchronizedService.synMethodTwo();
	}
	
}




@Service
public class SynchronizedService{

	@Transactional(readOnly = false,rollbackFor = SLException.class)
	public void synMethodTwo() throws SLException{
			
	}
	
}

 

备注:关于Synchronized的学习可以参考

1:Synchronized 方法和代码块的那个用法好一些比较。

2:关键字Synchronized的使用注意事项。

 

<think>嗯,用户现在的问题是关于在高并发场景下,Java中多个端口调用同一接口访问数据库,如何选择合适的锁机制来保证数据一致性。之前我已经给出了一些方案,比如使用多端口监听负载均衡,还提到了悲观锁乐观锁的应用。现在用户参考了提供的引用内容,特别是引用34,这些涉及悲观锁乐观锁的具体实现,以及MyBatis的SQL映射。 首先,我需要明确用户的需求。用户是在多个端口(可能是多个服务实例或同一应用的不同端口)同调用同一个接口,这种情况下,并发访问数据库可能导致数据不一致。用户想知道在这种情况下应该选择哪种锁机制,以及如何在Java中实现。 我需要回顾之前的回答,确保一致性。之前提到了多端口监听的配置、数据库连接池的设置,以及并发控制策略,包括悲观锁乐观锁。现在用户进一步询问锁机制的选择,需要更详细地比较这两种锁的适用场景、实现方式,并结合高并发的情况给出建议。 引用3提到了悲观锁乐观锁的区别,引用4中的代码示例展示了使用MyBatis进行数据库操作,如何在Service层实现悲观锁(通过selectForUpdate)乐观锁(版本号控制)。用户可能希望了解如何在实际代码中应用这些锁,特别是在多个端口调用的环境下。 用户可能关心的问题包括: 1. 在高并发下,哪种锁机制更适合? 2. 如何具体实现这两种锁,尤其是在使用MyBatis的情况下? 3. 是否有其他并发控制机制可以结合使用? 4. 如何处理分布式环境下的锁问题,比如多个实例的情况? 需要结合引用中的信息,比如Shoal框架(引用1)用于集群支持,但用户的问题主要集中在锁机制的选择,所以可能不需要深入讨论集群框架,除非涉及到分布式锁。但用户当前的问题可能还局限在单应用多端口的情况,或者需要考虑分布式锁的情况。 另外,用户提到多个端口调用同一接口,可能涉及到多个线程或进程同访问数据库,因此需要考虑数据库层面的锁机制,以及应用层的同步机制。 在之前的回答中,我提到了使用synchronized关键字数据库的悲观锁、乐观锁。现在需要更详细地分析这些方法的优缺点,并结合高并发场景给出建议,比如乐观锁在冲突较少性能更好,而悲观锁适合冲突较多的情况。 还需要考虑事务的管理,比如Spring的@Transactional注解,如何与锁机制配合使用,避免事务失效的问题。引用4中的代码示例使用@Transactional,并在更新库存检查版本号,这涉及到乐观锁的实现。 可能还需要提到分布式锁,比如使用Redis或ZooKeeper,但用户的问题是否涉及到分布式环境呢?如果多个端口属于不同的服务实例,那么可能需要分布式锁;如果是同一实例的不同端口,可能只需要JVM层面的锁。需要澄清这一点,但根据用户的问题描述,可能更偏向于同一应用的多端口,因此可能不需要分布式锁,但需要考虑数据库层面的锁机制。 总结来说,回答需要涵盖以下几点: 1. 分析高并发下数据一致性的挑战。 2. 比较悲观锁乐观锁的适用场景、实现方式及优缺点。 3. 提供具体代码示例,特别是使用MyBatisSpring的情况。 4. 讨论分布式环境下的锁机制(如果需要)。 5. 结合之前的回答,确保建议一致,并参考提供的引用内容。 需要确保回答结构清晰,逐步引导用户理解不同锁机制的选择,并结合实际代码示例,帮助用户具体实施。同,需要提到性能优化,比如批量处理、连接池配置等,以应对高并发压力。</think>根据高并发场景下多端口调用同一接口访问数据库的需求,结合锁机制选择数据一致性要求,以下是针对性解决方案: ### 一、高并发场景核心矛盾分析 1. **数据竞争本质**:多线程/多进程同修改同一条数据记录产生的写冲突 2. **端口调用特点**:多个入口请求可能绕过JVM锁直达数据库,需数据库级锁控制[^3] 3. **性能平衡点**:吞吐量与数据安全性需要根据业务容忍度进行权衡 ### 二、锁机制选型对比 | 锁类型 | 实现方式 | 适用场景 | 性能影响 | 代码示例参考 | |--------|---------------------------|---------------------------|------------|--------------| | **数据库悲观锁** | `SELECT ... FOR UPDATE` | 写多读少、冲突率高 | 较高 | [^4] | | **数据库乐观锁** | 版本号字段+CAS机制 | 读多写少、冲突率低 | 较低 | [^3] | | **JVM级锁** | `synchronized`/`ReentrantLock` | 单实例多端口场景 | 中等 | [^3] | | **分布式锁** | Redis Redisson/Zookeeper | 多实例集群环境 | 网络依赖 | [^1] | ### 三、具体实现方案 #### 方案1:数据库悲观锁(推荐用于强一致性场景) ```java // MyBatis Mapper接口定义 @Update("UPDATE product SET stock = stock - #{quantity} WHERE id = #{id} AND stock >= #{quantity}") int pessimisticLock(@Param("id") Long id, @Param("quantity") Integer quantity); // Service层使用示例 @Transactional public boolean deductStock(Long productId, Integer quantity) { int rows = productMapper.pessimisticLock(productId, quantity); return rows > 0; } ``` *特点*:通过数据库行锁直接保证原子性,但可能引发死锁需设置事务[^4] #### 方案2:乐观锁+重试机制(推荐用于高吞吐场景) ```java // 实体类添版本字段 public class Product { private Long id; private Integer stock; @Version private Integer version; } // MyBatis更新语句 <update id="optimisticUpdate"> UPDATE product SET stock = #{stock}, version = version + 1 WHERE id = #{id} AND version = #{version} </update> // 带重试的业务逻辑 @Retryable(maxAttempts = 3, backoff = @Backoff(delay = 100)) public void processWithRetry(Product product) { Product current = productMapper.selectByPrimaryKey(product.getId()); if(current.getStock() < product.getQuantity()) { throw new BusinessException("库存不足"); } current.setStock(current.getStock() - product.getQuantity()); int rows = productMapper.optimisticUpdate(current); if(rows == 0) { throw new OptimisticLockingFailureException("版本冲突"); } } ``` *特点*:需配合Spring Retry实现自动重试,版本冲突自动回滚[^3] ### 四、混合锁策略设计 ```mermaid graph TD A[请求进入] --> B{冲突概率判断} B -->|高冲突| C[使用悲观锁] B -->|低冲突| D[使用乐观锁] C --> E[直接锁定资源] D --> F[版本校验+重试] E --> G[完成业务操作] F -->|失败| H[重试机制] H -->|超过阈值| I[降级处理] ``` ### 五、性能优化关键点 1. **锁粒度控制**:通过`WHERE`条件缩小锁定范围 ```sql UPDATE product SET stock = stock - 1 WHERE id IN (1,2,3) AND stock > 0 ``` 2. **批量处理优化**:减少单次操作锁持有间 ```java @Transactional public void batchDeduct(List<Product> products) { products.forEach(p -> { productMapper.optimisticBatchUpdate(p); }); } ``` 3. **连接池配置**:根据并发量动态调整连接数 ```yaml spring: datasource: hikari: maximum-pool-size: 50 minimum-idle: 10 ``` ### 六、异常处理建议 1. **死锁检测**:配置数据库死锁超(MySQL示例) ```sql SET innodb_lock_wait_timeout = 20; ``` 2. **熔断降级**:使用Resilience4j实现故障隔离 ```java CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("database"); Supplier<String> decoratedSupplier = CircuitBreaker .decorateSupplier(circuitBreaker, () -> service.deductStock()); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值