2017-11-13 5 views
2

난 아주 간단한 웹 서비스가 있다고 가정 해 봅시다. 단 하나의 태스크는 엔드 포인트가 호출 된 횟수를 세는 것입니다. 엔드 포인트는 /hello입니다.메소드에 대한 동시 호출 수 계산

@Controller 
public class HelloController { 

    private int calls = 0; 

    @RequestMapping("/hello") 
    public String hello() { 
     incrementCalls(); 
     return "hello"; 
    } 

    private void incrementCalls() { 
     calls++; 
    } 
} 

지금이 한 두 사용자가 동시에 같은 시간에 /hello를 호출하지 않는 한 모든 잘 작동합니다. 그러나 /hello에 대한 병렬 호출이 발생하면 calls 변수는 한 번만 증가합니다 (잘못 입력하지 않은 경우). 그래서 분명히 어떤 종류의 동기화가 여기서 일어날 필요가 있습니다.

이 방법을 스레드로부터 안전하게 만드는 가장 좋은 방법은 무엇이 있을까요?

+2

'int' 대신'java.util.concurrent.atomic.AtomicInteger'를 사용하십시오. – Jesper

+2

콜 카운트에 원자 원 또는 정수를 추가하면 문제가 없습니다. 그렇지 않으면 잠금으로 통화 횟수 변수를 보호하십시오. –

+0

관련 항목 : https://stackoverflow.com/questions/16795303/must-spring-mvc-classes-be-thread-safe – assylias

답변

3

calls++이 예상하지 못한 동작을 일으킬 수있는 이유는 원자이 아닌 경우입니다. 원자 연산은 전체 연산이 다른 스레드에 의해 가로 챌 수없는 방식으로 발생합니다. Atomicity는 작업을 잠그거나 이미 원자 적 방식으로 수행하는 하드웨어를 사용하여 구현됩니다.

증가 작업은 아마 calls = calls + 1;의 바로 가기이기 때문에 예상대로 원자 연산이 아닐 가능성이 큽니다. 두 스레드가 증가하기 전에 calls에 대해 동일한 값을 검색한다는 것은 실제로 발생할 수 있습니다. 그런 다음 둘 다 이미 증가 된 값을 얻는 대신 동일한 값을 저장합니다.

get 및 increment를 원자 연산으로 전환하는 몇 가지 간단한 방법이 있습니다.

private void incrementCalls() { 
    calls++; 
} 

이 암시는 스레드가 방법을 입력 할 때마다에 속하는 HelloController 물체에 고정됩니다 :되지 않은 수입을 필요로하는 당신을위한 가장 간단한 하나는, 당신의 방법 synchronized하는 것입니다. 다른 스레드는 메서드를 시작하기 위해 잠금이 해제 될 때까지 기다려야합니다. 그러면 전체 메서드가 원자 연산으로 처리됩니다.

다른 방법은 원하는 코드 부분을 명시 적으로 동기화하는 것입니다.

private void incrementCalls() { 
    sychronized(this) { 
     calls++; 
    } 
} 

전체 방법 synchronized을 만들기 포장 단지 바로 가기되어 동기화가 상당히 시간과 공간이 고가이기 때문에 이것은 종종 원자 작업을 많이 할 필요가 없습니다 큰 방법에 대한 더 나은 선택이다 전체 내용은 synchronized(this)입니다.

java.util.concurrent.atomic.AtomicInteger은 정수 작업에서 수행하고자하는 대부분의 작업을 원자 단위 작업으로 처리하기 위해 동기화를 처리합니다. 이 경우 getAndAdd(1) 또는 getAndIncrement()으로 전화 할 수 있습니다. 이것은 아마도 중괄호의 수를 줄이고 신중하게 설계된 라이브러리 함수를 사용하므로 코드를 읽기 쉽게 유지하는 측면에서 가장 깨끗한 솔루션 일 것입니다.