목록Java (19)
백엔드 엔지니어 이재혁
Team 테이블과 Team에 연관관계에 있는 Member 테이블이 있다고 하자. Team 테이블에서 Team A, Team B, Team C의 모든 Member를 조회하고 싶다면, 보통 다음과 같은 쿼리를 실행할 것이다.SELECT m.* FROM Team tLEFT JOIN Member m ON t.id = m.team_idWHERE t.name IN ('Team A', 'Team B', 'Team C'); 하지만 JPA는 이렇게 조회하지 않는다. JPA의 실행방법SELECT * FROM TeamWHERE name IN ('Team A', 'Team B', 'Team C');SELECT * FROM MemberWHERE team_id = 1;SELECT * FROM MemberWHERE team_id ..
ExecutorService 종료`ExecutorService`를 종료하는 방법은 크게 두 가지 방법이 있다.es.shutdown(): 논블로킹 우아한 종료. (graceful shutdown)새 작업이 추가되는 것만 막고 기존에 실행/대기 중인 작업이 모두 끝날 때까지 기다린 다음 스레드풀을 제거함.es.shutdownNow(): 논블로킹 강제 종료.대기 중인 작업을 모두 작업 큐에서 제거하고, 실행 중인 작업 모두에 인터럽트를 걸어 종료시킨다.반환값으로 대기하고 있던 작업들을 반환. `List``ExecutorService`가 종료된 것을 확인한 다음에 어떤 작업을 해야 한다면, 즉 블로킹 메서드가 필요하다면, `es.awaitTermination()`을 사용해 대기하면 된다. 우아한 종료 구현 (`..
Controller 테스트 코드는 HTTP 요청을 받는 경우들을 확인하면 된다.Controller에서 응답이 잘 되는지 혹은 잘못된 요청에 대한 차단이 되는지 확인하자. 말이 어렵다면, 예시를 한 번 보자. 1. 인증/권한 검증@Test@DisplayName("일반 회원은 자격증 리스트를 볼 수 없다")void getCertList_NonAdmin() throws Exception { // Given Role nonAdminRole = new Role(); nonAdminRole.setRname("ROLE_USER"); User nonAdminUser = new User(); nonAdminUser.setRole(nonAdminRole); when(userService.g..
`Thread` 객체를 직접 사용하는 것은 생성 비용 문제와 관리 문제로 인해 비효율적일 때가 많다. 1. 스레드 생성 비용 문제`Thread` 객체는 다음과 같은 이유로 일반적인 Java 객체를 생성하는 비용보다 훨씬 많은 비용이 든다. 메모리 할당: 스레드는 자신만의 call stack을 가지고 있고, 스레드 생성시 call stack을 위한 메모리 공간을 확보해야 한다.시스템 콜: 스레드를 생성하는 작업은 운영 체제의 커널 수준에서 작동해 "시스템 콜"을 통해 처리된다. CPU와 메모리 리소스를 소모한다.운영체제 스케쥴러 설정: 새로운 스레드가 생성되면 CPU 스케쥴러가 스레드 실행 순서를 스케쥴링 알고리즘에 맞게 조정한다. 이런 과정에서 추가적인 오버헤드가 발생할 수 있다.스레드 생성에 드는 비용..
기본 컬렉션 프레임워크는 동시성 문제를 일으킬 수 있다. 예시) `ArrayList`의 `add()` 메서드는 데이터를 추가할 뿐 아니라, `size`에 새로운 값도 대입하는 등 원자적인 연산이 아니다.// ArrayList 클래스의 add 메서드들 중 하나private void add(E e, Object[] elementData, int s) { if (s == elementData.length) elementData = grow(); elementData[s] = e; size = s + 1;} 해결 방법1. 프록시 패턴동시성 처리가 되지 않은 기본 객체는 그대로 두고, 아래와 같이 그 객체의 인터페이스 구현체를 만든다.public class SyncProxyList i..
CAS 연산을 활용해 동기화 락 (synchronized, Lock 인터페이스) 없이 락을 구현해보자. 우선 잘못 만든 예제를 확인해보자.public class SpinLockBad { private volatile boolean lock = false; public void lock() { log("락 획득 시도"); while (true) { if (!lock) { // 1. 락 사용 여부 확인 // sleep(100); // 문제 상황 확인용, 스레드 대기 lock = true; // 2. 락의 값 변경 break; // while 탈출 ..
앞서, CAS 연산은 비교와 변경을 하나로 합친 원자적 연산으로 처리해주는 기능이라고 배웠다. 이번에는 그럼 CAS 연산을 활용해 멀티스레드 환경에서 락 없이 안전한 값 변경을 구현해보자. incrementAndGet 직접 구현해보기private static int incrementAndGet(AtomicInteger atomicInteger) { int getValue; boolean result = false; do { getValue = atomicInteger.get(); // 1 log("getValue: " + getValue); result = atomicInteger.compareAndSet(getValue, getValue + 1); ..
Atomic(원자적) 연산이란, 더이상 쪼갤 수 없는 연산을 의미한다. 원자적 연산의 예시`i=10;` 같은 것 원자적 연산이 아닌 것`i++;` (`i = i + 1;`)i의 값을 읽는 과정그 값에 1을 더하는 과정i에 그 값을 다시 대입하는 과정세 가지 단계로 나눌 수 있다. 다음과 같이 `i++;` 작업을 두 스레드가 동시에 할 때를 살펴보자처음에 i = 0이라고 가정하겠다.스레드1: i = i + 1 연산 수행스레드2: i = i + 1 연산 수행스레드1: i의 값을 읽는다. i는 0이다.스레드2: i의 값을 읽는다. i는 0이다.스레드1: 읽은 0에 1을 더해서 1을 만든다.스레드2: 읽은 0에 1을 더해서 1을 만든다.스레드1: 더한 1을 왼쪽의 i변수에 대입한다.스레드2: 더한 1을 왼쪽의 ..