티스토리 뷰
반응형
getConnection
public Connection getConnection(final long hardTimeout) throws SQLException
{
suspendResumeLock.acquire();
final long startTime = currentTime();
try {
long timeout = hardTimeout;
do {
PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS);
if (poolEntry == null) {
break; // We timed out... break and throw exception
}
final long now = currentTime();
if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > aliveBypassWindowMs && !isConnectionAlive(poolEntry.connection))) {
closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE);
timeout = hardTimeout - elapsedMillis(startTime);
}
else {
metricsTracker.recordBorrowStats(poolEntry, startTime);
return poolEntry.createProxyConnection(leakTaskFactory.schedule(poolEntry), now);
}
} while (timeout > 0L);
metricsTracker.recordBorrowTimeoutStats(startTime);
throw createTimeoutException(startTime);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new SQLException(poolName + " - Interrupted during connection acquisition", e);
}
finally {
suspendResumeLock.release();
}
}
connectionBag.borrow(timeout, MILLISECONDS)
public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException
{
// Try the thread-local list first
final List<Object> list = threadList.get();
for (int i = list.size() - 1; i >= 0; i--) {
final Object entry = list.remove(i);
@SuppressWarnings("unchecked")
final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry;
if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
return bagEntry;
}
}
// Otherwise, scan the shared list ... then poll the handoff queue
final int waiting = waiters.incrementAndGet();
try {
for (T bagEntry : sharedList) {
if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
// If we may have stolen another waiter's connection, request another bag add.
if (waiting > 1) {
listener.addBagItem(waiting - 1);
}
return bagEntry;
}
}
listener.addBagItem(waiting);
timeout = timeUnit.toNanos(timeout);
do {
final long start = currentTime();
final T bagEntry = handoffQueue.poll(timeout, NANOSECONDS);
if (bagEntry == null || bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
return bagEntry;
}
timeout -= elapsedNanos(start);
} while (timeout > 10_000);
return null;
}
finally {
waiters.decrementAndGet();
}
}
초반 부분
// Try the thread-local list first
final List<Object> list = threadList.get();
for (int i = list.size() - 1; i >= 0; i--) {
final Object entry = list.remove(i);
@SuppressWarnings("unchecked")
final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry;
if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
return bagEntry;
}
}
ThreadLocalMap & ThreadLocal
- ThreadLocalMap
public class Thread implements Runnable {
private static native void registerNatives();
static {
registerNatives();
}
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap은 Thread 필드로 가지고 있으며, 이는 특정 스레드의 정보를 ThreadLocal에서 직접 호출할 수 있도록 되어 있다.
ThreadLocal을 이용하면 쓰레드 영역에 변수를 설정할 수 있기 때문에, 특정 쓰레드가 실행하는 모든 코드에서 설정된 변수 값을 사용할 수 있게 된다.
참고: ThreadLocal
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
for (int i = list.size() - 1; i >= 0; i--)
뒤에서 부터 읽는 것은 바로 삭제하기 때문이다. 만약에 0부터 시작했다면 삭제했을 때 데이터의 순서보장이 이루어지지 않기 때문에 뒤에서부터 데이터를 읽는 것으로 확인된다.
final Object entry = list.remove(i);
WeakReference
LRU 캐시와 같은 애플리케이션에서는 softly reachable 객체보다는 weakly reachable 객체가 유리하므로 LRU 캐시를 구현할 때에는 대체로 WeakReference를 사용한다
- weakly reachable: strongly reachable 객체도 softly reachable 객체도 아닌 객체 중에서, phantom reference 없이 weak reference만 통과하는 참조 사슬이 하나라도 있는 객체
나머지 borrow 부분은 다른 스레드가 먼저 들어올 때 처리와 마지막에 해당 타임아웃을 검사하면서 해당 스레드가 존재할 때 그 스레드를 가져오고 존재하지 않는다면 null 값을 반환하는 작업을 하고 있다.
getConnection 핵심 부분
if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > aliveBypassWindowMs && !isConnectionAlive(poolEntry.connection))) {
closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE);
timeout = hardTimeout - elapsedMillis(startTime);
}
else {
metricsTracker.recordBorrowStats(poolEntry, startTime);
return poolEntry.createProxyConnection(leakTaskFactory.schedule(poolEntry), now);
}
참고: understanding-hikaricps-connection-pooling-behaviour
코드를 그래프로 표시했을 때 다음과 같이 작동됨을 알 수 있습니다.
반응형
'JVM > Spring' 카테고리의 다른 글
[Spring] @Scheduled, @EnableScheduling (0) | 2022.10.01 |
---|---|
[Spring] Scope (Prototype & Singleton) (2) | 2022.09.13 |
[Hikari CP] 光 살펴보기 - 3 (0) | 2022.04.28 |
[Hikari CP] 光 살펴보기 - 2 (0) | 2022.04.27 |
[Hikari CP] 光 살펴보기 - 1 (0) | 2022.04.26 |
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- headers
- docker
- Pattern
- Python
- 알고리즘
- Spring
- thread
- 파이썬
- Linux
- 그래프
- django
- DRF
- setattr
- PostgreSQL
- Celery
- dockerignore
- postgres
- Command Line
- docker-compose
- 카카오
- 백준
- ubuntu
- Collections
- 자바
- 2021 KAKAO BLIND RECRUITMENT
- Java
- env
- BFS
- 프로그래머스
- 면접
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
글 보관함