Lombok之@Synchronized使用

一. 为什么要用@Synchronized?

synchronized是线程安全中一个重要的关键字,它是一种同步锁,主要用来保证在同一个时刻,只有一个线程可以执行某个方法或者某段代码块。一般使用synchronized去锁住代码块,而不是方法,因为锁住代码块效率更高。

@Synchronized是synchronized方法修饰符的更安全的变体。与一样synchronized,注释只能在静态方法和实例方法上使用。它的操作类似于synchronized关键字,但是它锁定在不同的对象上。关键字锁定在上this,但注释锁定在名为的字段上$ lock,该字段是私有的。
如果该字段不存在,则会为您创建。如果对static方法进行注释,则注释将锁定在名为的静态字段上$ LOCK。

如果需要,可以自己创建这些锁。在$ lock和$ LOCK领域会当然不会,如果你已经自己原创生成的。您还可以通过将其指定为@Synchronized注释的参数来选择锁定另一个字段。在此用法变型中,不会自动创建字段,并且您必须自己明确创建它们,否则会发出错误。

二. @Synchronized如何使用?

在这里插入图片描述
如果 l o c k 和 / 或 是 lock和/或是 lock/LOCK自动生成的,则字段将使用一个空Object[]数组进行初始化,而不仅仅是new Object()显示该模式在实际使用中的大多数代码片段。Lombok这样做是因为新对象不可序列化,而0大小的数组则可序列化。因此,使用@Synchronized不会阻止您的对象被序列化。

@Synchronized类 中至少有一个方法意味着将有一个锁字段,但是如果以后删除所有这些方法,将不再有锁字段。这意味着您预定的serialVersionUID更改。如果您打算通过Java的序列化机制长期存储它们,我们建议您始终serialVersionUID向类中添加a 。如果这样做,@Synchronized从方法中删除所有注释不会破坏序列化。

三. @Synchronized源码

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Synchronized {
	/**
	 * Optional: specify the name of a different field to lock on. It is a compile time error if this field
	 * doesn't already exist (the fields are automatically generated only if you don't specify a specific name.
	 * 
	 * @return Name of the field to lock on (blank = generate one).
	 */
	String value() default "";
}

注解属性:value是指自定义锁的字段名。

四. 特别说明

本文已经收录在Lombok注解系列文章总览中,并继承上文中所提的特别说明。
源码地址:gitee

<think>我们正在讨论的是Java中的两个注解:@Scheduled和@Synchronized。这两个注解分别来自Spring框架和Project Lombok库。 1. **@Scheduled**:Spring框架中的注解,用于任务调度。标记有@Scheduled的方法会被定期调用,可以配置固定速率、固定延迟或cron表达式。 2. **@Synchronized**:Project Lombok提供的注解,用于方法级别的同步。它会在方法上生成一个同步块,确保同一时间只有一个线程可以执行该方法。 **使用场景**: - 当我们需要定期执行某个任务(使用@Scheduled),并且这个任务需要是线程安全的(使用@Synchronized)时,这两个注解可能会一起使用。 **注意事项**: - **锁对象的选择**:@Synchronized默认使用当前实例作为锁(即`synchronized(this)`),但如果是静态方法,则使用当前类的Class对象作为锁。注意,如果多个方法使用同一个锁对象,可能会导致不必要的阻塞。 - **性能考虑**:同步会带来性能开销,特别是在高频率调度的任务中。因此,应确保同步块尽可能小,避免在同步块中执行耗时操作。 - **死锁风险**:避免在同步方法中调用其他同步方法,否则可能引发死锁。 - **Spring代理机制**:由于Spring的AOP代理机制,@Scheduled注解是通过代理对象调用的,而@Synchronized是在目标对象上实现的。因此,这两个注解的组合使用在大多数情况下是可行的,但要注意代理模式(如JDK动态代理和CGLIB代理)可能对锁的影响。 - **集群环境**:在分布式环境中,使用@Synchronized只能保证单个JVM内的同步。如果需要在多个实例之间同步,需要使用分布式锁。 **示例代码**: ```java import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import lombok.Synchronized; @Component public class ScheduledTask { // 使用@Scheduled注解,每5秒执行一次 @Scheduled(fixedRate = 5000) @Synchronized // 确保同一时间只有一个线程执行该方法 public void doScheduledTask() { // 执行需要同步的任务逻辑 // 注意:这里的方法会在多个线程中调用(由调度器触发),但@Synchronized保证了同一时刻只有一个线程执行 } } ``` **替代方案**: - 如果任务的执行时间可能超过调度间隔,可以考虑使用其他同步机制(如ReentrantLock)或调整调度策略(例如设置`@Scheduled(fixedDelay)`等待上次执行完成后再开始下一次)。 - 在集群环境中,需要使用分布式锁(如Redis锁、ZooKeeper等)来替代@Synchronized。 **总结**: - 在单机环境下,结合使用@Scheduled和@Synchronized可以确保定时任务的线程安全。 - 在分布式系统中,需要额外的机制来实现跨JVM的同步。 - 注意锁的粒度和性能影响。 §§相关问题§§ 1. @Scheduled注解有哪些参数可以配置? 2. 除了@Synchronized,还有哪些实现线程安全的方法? 3. 在Spring中,如何实现分布式定时任务? </think>### @Scheduled 与 @Synchronized 结合使用指南 #### 一、核心功能说明 1. **@Scheduled** Spring框架的任务调度注解,用于定时执行方法,支持三种模式: - `fixedRate`:固定频率执行(上次开始后间隔) - `fixedDelay`:固定延迟执行(上次结束后间隔) - `cron`:Cron表达式控制 2. **@Synchronized** Lombok提供的线程同步注解,本质是为方法生成`synchronized`代码块: ```java // Lombok编译后实际代码 public synchronized void method() { // ... } ``` #### 二、组合使用场景 ✅ **适用场景**: 1. **共享资源保护** 定时任务需修改共享数据(如全局计数器、缓存数据)时防止并发冲突: ```java @Component public class DataSyncService { private int counter = 0; @Scheduled(fixedRate = 5000) @Synchronized public void syncData() { counter++; // 安全操作共享变量 // 数据同步逻辑... } } ``` 2. **第三方API调用限流** 避免高频触发外部API的速率限制: ```java @Scheduled(cron = "0/10 * * * * ?") @Synchronized public void callExternalAPI() { // 保证每分钟最多6次调用 } ``` 3. **文件/IO操作防冲突** 多任务禁止同时读写同一文件: ```java @Scheduled(fixedDelay = 10000) @Synchronized public void writeLogFile() { // 独占文件写入 } ``` #### 三、注意事项与风险 ⚠️ **关键问题**: 1. **死锁风险** 嵌套调用同步方法时可能死锁: ```java @Synchronized public void methodA() { methodB(); // 可能触发死锁 } @Synchronized public void methodB() { // ... } ``` 2. **性能下降** 高频调度任务中,锁竞争会导致线程阻塞: ``` +------------------+---------------------+ | 调度频率 | 吞吐量下降幅度 | +------------------+---------------------+ | 100ms/次 | 最高达40% | | 1s/次 | <10% | +------------------+---------------------+ ``` 3. **集群环境失效** `@Synchronized`仅在单JVM内生效,分布式环境需改用: - Redis分布式锁 - ZooKeeper协调 - Quartz集群调度 4. **锁粒度问题** 默认使用`this`对象锁,高并发时建议细化锁对象: ```java private final Object lock = new Object(); @Scheduled(...) public void task() { synchronized(lock) { // 更细粒度的锁 // ... } } ``` #### 四、最佳实践 1. **替代方案推荐** - 使用`ReentrantLock`替代同步块(支持超时机制): ```java private final Lock lock = new ReentrantLock(); @Scheduled(...) public void safeTask() { if (lock.tryLock(1, TimeUnit.SECONDS)) { try { // ... } finally { lock.unlock(); } } } ``` 2. **调度配置建议** - 避免`fixedRate`搭配同步:上次执行未完成时新任务会阻塞 - 优先使用`fixedDelay`:确保每次执行间隔可控 3. **监控必备** 添加任务执行时长日志: ```java @Scheduled(...) @Synchronized public void monitoredTask() { long start = System.currentTimeMillis(); // ...业务逻辑 log.info("Task cost: {}ms", System.currentTimeMillis()-start); } ``` > **总结**:在单机低并发场景下可谨慎使用组合注解,分布式环境或高性能要求场景需改用分布式锁+异步队列方案。Spring官方文档建议任务调度应设计为幂等操作[^1],减少对锁的依赖。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值