-
Notifications
You must be signed in to change notification settings - Fork 0
이지표 4주차 학습일지
- 서로 붙어있는 여러 개의 메세지를 포함하여 하나의 복합 메시지로 보내진다.
- 메시지는 “Content-type:” 헤더에 boundary 파라미터를 포함한다.
- boundary는 메시지 파트를 구분하는 역할을 하며, 메시지 시작과 끝 부분도 나타난다.
- InputStream: 다양한 소스로 부터 바이트 형태의 이진 데이터를 읽는다.
- InputStreamReader: 문자나 텍스트를 직접 다룬다.
- InputStreamReader는 바이트 스트림을 바이트 코드로 변환하는 브릿지 역할을 한다.
- InputStream을 나타내는 모든 클래스의 상위 클래스이다.
- 이진 데이터를 다룰 때는 InputStream을, 문자 스트림을 다룰 때는 InputStreamReader를 사용하는 게 좋다.
- Driver
- 데이터베이스와 Java 애플리케이션 사이의 통신을 담당한다.
- Connection
- 데이터베이스와의 연결을 나타내는 인터페이스이다.
- DriverManager.getConnection()메서드를 통해 얻을 수 있다.
- Statement
- SQL 쿼리를 실행하기 위한 인터페이스
- Connection객체로 부터 createStatement() 메서드를 통해 생성한다.
- PreparedStatement
- Statement의 하위 인터페이스로, 미리 컴파일 SQL문을 나타낸다.
- SQL injection 방지에 도움을 준다.
- Connection 객체의 preparedStatement() 메서드로 생성한다.
- ResultSet
- SQL 쿼리 실행 결과를 나타내는 인터페이스이다.
- Statement또는 PreparedStatement의 executeQuery()메서드 실행 후 반환된다.
Java Memory Model을 이해하기 전에 2가지 개념을 이해해야한다.
일반적으로 명령어가 동일한 순서로 실행되는 것을 예상한다. 하지만, 컴파일러나 JVM 혹은 CPU는 성능을 향상시키기 위해 명령의 순서를 변경할 가능성이 있다. 프로그램의 의미는 동일하므로, 프로그램 출력도 동일하게 유지된다.
a = 3;
b = 2;
a = a + 1;
위 프로그램은 실제로 어떻게 수행될까?
-> a를 Load한다.
-> 3을 설정한다.
-> a에 저장한다.
-> b를 Load한다.
-> 2를 설정한다.
-> b에 저장한다.
-> a를 Load한다.
-> 4를 설정한다.
-> a에 저장한다.
아마 많은 사람들은 아래와 같이 수행될 것을 예상한다. 하지만 여기에 a가 2번 Load된것을 확인할 수 있다.
a = 3;
a = a + 1;
b = 2;
-> a를 Load한다.
-> 3을 설정한다.
-> 4를 설정한다.
-> a에 저장한다.
-> b를 Load한다.
-> 2를 설정한다.
-> b에 저장한다.
이 경우 a를 Load를 한번만 한다. 컴파일러, JVM 혹은 CPU는 이런식으로 연산을 최적화 시킨다.
일반적으로 동시성 혹은 동시 프로그래밍이라고 불리는 멀티 스레드 어플리케이션 측면에서만 적용가능하다.
CPU에 4코어가 있다고 가정하겠다. 각 core에는 registers가 있다. 이 레지스터는 작지만 코어내에 있으므로 값을 저장하고, 다음 레이어에 저장하는 방식이 가장 빠르다. 각 코어마다 메모리 영역인 L1 cache가 있고, 두 코어가 공유할 수 있는 L2 cache가 있다. 그 다음 4코어에서 공유할 수 있는 L3 cache와 RAM이 있다.
public class FieldVisibility {
int x = 0;
public void writerThread() {
x = 1;
}
public void readerThread() {
int r2 = x;
}
}
2개의 thread가 프로그램을 실행한다고 가정한다. writer-thread는 writerThread()
만 실행한다고 가정하고, reader-thread는 readerThread()
만 실행한다고 가정한다. 그리고 두 스레드가 동시에 병렬로 실행한다고 가정한다.
core1에서 x를 1로 바꾸는 연산은 local cache에서만 일어나게 된다. x = 1이 shared cache에 공유되지 않을 가능성이 많다. core2에서 값을 가져올때, x의 old value인 0을 가져오고, r2에 0을 할당할 가능성이 높다.
그러면 잘못된 값을 할당할 문제가 발생할 수 있다. 이 문제를 peel visibility issue라고 한다.
자바는 volatile이라는 키워드를 사용하여 해당 문제해결을 제공한다.
public class FieldVisibility {
volatile int x = 0;
public void writerThread() {
x = 1;
}
public void readerThread() {
int r2 = x;
}
}
volatile의 키워드를 사용하면 JVM은 x가 1로 변경 될 시, shared cache에 flush를 한다. 이런식으로 항상 업데이트 된 값을 얻도록 보장한다.
JMM은 변수의 가시성 또는 가시성을 보장하는 사양, 규칙 집합에 지나지 않는다. 성능 향상을 위해 명령을 재정렬시 필드의 순서를 바꾸므로, 규칙을 재대로 정하면, 다른 JVM에서 실행할 때에도 동일한 결과를 얻게 된다. 따라서 JMM은 모든 JVM에서 구현되어야 한다.
public class VolatileFieldsVisibility {
int a = 0, b = 0, c = 0;
volatile int x = 0;
public void writeThread() {
a = 1;
b = 1;
c = 1;
x = 1;
}
public void readerThread() {
int r2 = x;
int d1 = a;
int d2 = b;
int d3 = c;
}
}
여기에 volatile 키워드가 없는 a, b, c가 있고, volatile키워드가 붙어져 있는 x가 있다. JMM에는 규칙이 있는데, volatile키워드가 붙어져 있는 value가 있기 전에 읽어들이는 모든 field는 이후, x값을 읽어들이고 다른 스레드에서 볼 수 있어야 한다. 따라서 이전에 발생된 모든 내용은 이 후에 표시되어야 한다.
사실 이건 volatile키워드에서만 적용되는 건 아니다. Synchronized, Locks, Concurrent collections, Thread operations(join, start), final fileds(special behavior)에서도 나타난다.
public class SynchronizedFieldsVisibility {
int a = 0, b = 0, c = 0;
volatile int x = 0;
public void writeThread() {
a = 1;
b = 1;
c = 1;
synchronized (this) {
x = 1;
}
}
public void readerThread() {
synchronized (this) {
int r2 = x;
}
int d1 = a;
int d2 = b;
int d3 = c;
}
}
public class SynchronizedFieldsVisibility {
int a = 0, b = 0, c = 0;
volatile int x = 0;
public void writeThread() {
synchronized (this) {
a = 1;
b = 1;
c = 1;
x = 1;
}
}
public void readerThread() {
synchronized (this) {
int r2 = x;
int d1 = a;
int d2 = b;
int d3 = c;
}
}
}
public class LockVisibility {
int a = 0, b = 0, c = 0, x = 0;
Lock lock = new ReeentrantLock();
public void writeThread() {
lock.lock();
a = 1;
b = 1;
c = 1;
x = 1;
lock.unlock();
}
public void readerThread() {
lock.lock();
int r2 = x;
int d1 = a;
int d2 = b;
int d3 = c;
lock.unlock();
}
}
public class VolatileVisibility {
boolean flag = true;
public void writeThread() {
flag = false;
}
public void readerThread() {
while (flag) {
// do some thing
}
}
}
// wrong
// flush 하기 전까지 무한 루프
public class VolatileVisibility {
volatile boolean flag = true;
public void writeThread() {
flag = false;
}
public void readerThread() {
while (flag) {
// do some thing
}
}
}
// true
// 바로 스탑