Spring Boot 인터페이스 방어: 동시성 문제를 방지하는 실용적인 예

Spring Boot 인터페이스 방어: 동시성 문제를 방지하는 실용적인 예

이 기사에서는 Spring Boot에서 인터페이스 동시성 문제를 피하는 방법을 살펴보겠습니다. 높은 동시성 시나리오에서 올바르게 처리되지 않으면 요청이 데이터 불일치, 리소스 경쟁 및 성능 저하로 이어질 수 있습니다. 이러한 문제를 해결하기 위해 동기화 및 잠금을 사용하는 방법을 보여주기 위해 실용적인 사례를 사용할 것입니다.

1. 간단한 Spring Boot 프로젝트 생성

먼저 간단한 Spring Boot 프로젝트를 만들고 간단한 REST 인터페이스를 추가합니다. 이 예에서는 은행 계좌 이체 작업을 시뮬레이션합니다.

Account다음과 같은 항목 클래스를 만듭니다 .

public class Account {
    
    
    private Long id;
    private String owner;
    private BigDecimal balance;

    // 构造方法、getter 和 setter 省略
}

2. REST 인터페이스 생성

AccountController송금을 위한 인터페이스로 호출되는 REST 컨트롤러를 만듭니다 .

@RestController
@RequestMapping("/accounts")
public class AccountController {
    
    

    @Autowired
    private AccountService accountService;

    @PostMapping("/{fromAccountId}/transfer/{toAccountId}/{amount}")
    public ResponseEntity<Void> transfer(@PathVariable Long fromAccountId,
                                         @PathVariable Long toAccountId,
                                         @PathVariable BigDecimal amount) {
    
    
        accountService.transfer(fromAccountId, toAccountId, amount);
        return ResponseEntity.ok().build();
    }
}

3. 동시성 문제를 방지하기 위해 동기화 추가

AccountService클래스 에서 우리는 transfer한 계정에서 다른 계정으로 돈을 이체하는 데 사용되는 메서드를 구현합니다. 동시성 문제를 방지하기 위해 synchronized키워드를 한 번에 하나의 스레드만 전송 작업을 수행할 수 있도록 합니다.

@Service
public class AccountService {
    
    

    @Autowired
    private AccountRepository accountRepository;

    public synchronized void transfer(Long fromAccountId, Long toAccountId, BigDecimal amount) {
    
    
        Account fromAccount = accountRepository.findById(fromAccountId).orElseThrow();
        Account toAccount = accountRepository.findById(toAccountId).orElseThrow();

        if (fromAccount.getBalance().compareTo(amount) < 0) {
    
    
            throw new IllegalStateException("Insufficient balance");
        }

        fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
        toAccount.setBalance(toAccount.getBalance().add(amount));

        accountRepository.save(fromAccount);
        accountRepository.save(toAccount);
    }
}

위의 코드에서 synchronized키워드를 transfer메서드가 한 번에 하나의 스레드에서만 실행될 수 있도록 합니다. 이러한 방식으로 동시성으로 인한 데이터 불일치 및 리소스 경쟁 문제를 방지할 수 있습니다.

4. 요약

이 기사의 실습 사례를 통해 Spring Boot 인터페이스의 동시성 문제를 방지하는 방법을 배웠습니다. 동기화 및 잠금 메커니즘을 사용하면 한 번에 하나의 스레드만 중요한 작업을 수행할 수 있으므로 데이터 불일치 및 리소스 경쟁을 피할 수 있습니다. 그러나 동기화 및 잠금 메커니즘을 과도하게 사용하면 성능이 저하될 수 있으므로 적절하게 사용하십시오.

synchronized 키워드를 사용하는 것 외에도 다음과 같은 다른 동시성 제어 방법을 사용할 수 있습니다.

5. Java 동시성 라이브러리에서 잠금 사용

Java 동시성 라이브러리는 ReentrantLock과 같은 많은 고급 동시성 제어 도구를 제공합니다. ReentrantLock은 synchronized 키워드보다 뛰어난 유연성과 확장성을 제공합니다. 다음은 ReentrantLock을 사용하도록 재작성된 AccountService 클래스입니다.

@Service
public class AccountService {
    
    
	
	private final ReentrantLock lock = new ReentrantLock();

	@Autowired
	private AccountRepository accountRepository;

	public void transfer(Long fromAccountId, Long toAccountId, BigDecimal amount) {
    
    
		lock.lock();
		try {
    
    
			Account fromAccount = accountRepository.findById(fromAccountId).orElseThrow();
			Account toAccount = accountRepository.findById(toAccountId).orElseThrow();

			if (fromAccount.getBalance().compareTo(amount) < 0) {
    
    
				throw new IllegalStateException("Insufficient balance");
			}

			fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
			toAccount.setBalance(toAccount.getBalance().add(amount));

			accountRepository.save(fromAccount);
			accountRepository.save(toAccount);
		} finally {
    
    
			lock.unlock();
		}
	}
}

6. 낙관적 잠금 사용

낙관적 잠금은 여러 스레드가 작업을 동시에 수행하도록 허용하지만 작업을 커밋하기 전에 데이터가 변경되었는지 확인하여 동시성 문제를 방지하는 전략입니다. 데이터가 변경된 경우 작업이 다시 실행됩니다. 이 전략은 쓰기 작업보다 읽기 작업이 더 자주 발생하는 시나리오에 적합합니다.

Spring Boot에서 낙관적 잠금을 사용하려면 엔티티 클래스에 버전 필드를 추가하고 필드를 @Version 주석으로 표시할 수 있습니다. 다음은 수정된 계정 클래스입니다.

@Entity
public class Account {
    
    
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	private String owner;
	private BigDecimal balance;
	@Version
	private int version;

	// 构造方法、getter 和 setter 省略
}

이 예제에서 두 스레드가 동시에 동일한 Account 인스턴스를 업데이트하려고 하면 한 스레드만 변경 사항을 성공적으로 커밋하고 다른 스레드는 동시성 위반을 나타내는 OptimisticLockingFailureException을 수신합니다.

이 기사의 실습 사례를 통해 Spring Boot 인터페이스의 동시성 문제를 방지하는 방법을 배웠습니다. 실제 응용 프로그램에서 적절한 동시성 제어 전략을 선택하는 것은 데이터 일관성과 성능을 보장하는 데 중요합니다.

Je suppose que tu aimes

Origine blog.csdn.net/Susan003/article/details/130106763
conseillé
Classement