public class MainClass1{
public static void main(String[] args) {
Thread t1 = new Thread1();
Thread t2 = new Thread2();
Thread t3 = new Thread3();
t1.start();
t2.start();
t3.start();
}
}
class Thread1 extends Thread{
private int number = 1;
@Override
public void run() {
while(true){
Object1 o = Object1.getInstance();
int n = o.func(number);
System.out.println(n + " Thread1 의 넘버");
int n1 = o.sfunc(number);
System.out.println(n1 + " Thread1 의 넘버, 상태 공유 사용할 경우");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Thread2 extends Thread{
private int number = 2;
@Override
public void run(){
while(true){
Object1 o = Object1.getInstance();
int n = o.func(number);
System.out.println(n + " Thread2 의 넘버");
int n1 = o.sfunc(number);
System.out.println(n1 + " Thread2 의 넘버, 상태 공유 사용할 경우");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Thread3 extends Thread{
private int number = 3;
@Override
public void run(){
while(true){
Object1 o = Object1.getInstance();
int n = o.func(number);
System.out.println(n + " Thread3 의 넘버");
int n1 = o.sfunc(number);
System.out.println(n1 + " Thread3 의 넘버, 상태 공유 사용할 경우");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3개의 쓰레드를 생성한 후
각 쓰레드는 자신의 number 변수를 func(), sfunc() 메서드에 각각 넣은 후 출력
sfunc() - 자신의 번호를 출력하지 못하는 문제가 발생
이유는 공유 변수 oNumber가 스레드에 안전하지 않기 때문이다.
func() - 자신의 번호에 맞게 정확하게 출력한다.
스레드가 생성되면 스택이 새로 생성되고, 생성된 스택에서 메서드를 호출하여 사용한다.
각 스레드 스택의 스택 프레임은 지역 변수들이 사용된 후 소멸한다.
지역 변수가 Heap영역을 참조하여 사용한다면 공유 데이터 사용에 주의해야한다. (싱글톤은 공유 데이터를 갖지 않는 것이 원칙)
sfunc() - Object1의 상태 데이터(공유 데이터)를 사용하므로 스레드에 안전하지 못하다.
func() - Object1의 메서드만 사용하고 지역 변수는 자신이 넘겨주므로, Heap의 상태 데이터(공유 데이터)는 사용하지 않아 스레드에 안전하다.
Thread Safe
(1) 인스턴스 데이터를 사용하지 않는다. (싱글톤)
싱글톤 객체를 여러 쓰레드에서 사용할 때는 Heap의 인스턴스 데이터(상태 데이터)를 사용하지 않게 설계해야한다.
데이터는 스레드가 넘겨주고, Heap의 인스턴스는 메서드만 사용한다.
(2) 스레드마다 인스턴스를 생성하여 사용한다. (프로토타입)
Heap의 인스턴스 데이터를 사용해야 하는 경우엔 스레드마다 사용하는 인스턴스를 새로 생성하여 사용한다.
사용자에 따라 상태 정보가 달라지는 DTO, Entity가 싱글톤이 아닌 이유이기도 하다.
@RestControllerAdvice
public class GlobalAdvice {
@ExceptionHandler(value = Exception.class) // 예외 잡기
public ResponseEntity ex(Exception e){
e.printStackTrace();
return ResponseEntity.status(HttpStatus.OK).body("예외잡기");
}
}
예외를 일부러 발생시켰다.
@RestController
@RequestMapping("/adv")
public class AController {
@GetMapping("/get")
public String get() throws Exception{
throw new Exception();
}
}
예외가 글로벌Advice, @ExceptionHandler에 의해 처리되고, Body에 지정한 값을 응답하며 정상 동작한다.