본문 바로가기

IT/공부

[프로그래밍] event, synchronized, IO Block pool

Run - CPU만나는상태
자바 스레드 언어차원에서 지원

<<<event 이벤트>>>
전산에서는 어떤 동작을 하는 순간을 event이벤트라 한다. 홈페이지에서 ID, PW를 입력하고 로그인 버튼을 누르는 순간을 로그인 이벤트 발생이라 하고, 바탕화면에서 브라우저 아이콘을 더블클릭하는 순간을 이벤트 발생이라 하고, 메서드를 벗어나는 순간을 메서드 벗어나는 이벤트가 발생했다고 한다.

<<<메서드가 종료되면 Dead라고 한다>>>

<<<Thread, 동기화문제, synchronized 키워드>>>
Thread를 사용하는 이유는 OS차원에서 process 하나로 자원을 공유한다는 장점, Data를 쉽게 공유할 수 있다는 장점 때문이다.
그러나 공유할 때 동기화 문제가 발생하기도 한다. 동기화 문제는 한 번에 하나의 스레드를 처리하는 게 아니라 여러 스레드가 동시다발적으로 처리되기 때문에 발생하는 문제이다. 하나의 data file을 여러 사람이 동시에 접근해서 수정한다면 앞에 저장한 사람이 수정한 내용은 저장되지 않는 등 누락이나 중복 처리되고, data file의 신뢰성을 보장할 수 없다. thread가 실행되는 것은 thread scheduler가 작업하는 것이고 사람은 그 결과를 예측할 수 없기 때문에 thread가 run() 실행 중간에 thread가 run에서 runnable로 내려오는 경우가 많아서 동기화 문제가 발생하는 것이다.

동기화문제를 해결하기 위해 나온 Modifier keywordk가 synchronized이다. synchronized "메서드"(클래스, 필드, 메서드 중 메서드에만 사용 가능하다)를 가지고 있는 클래스는 인스턴스는 생성 시에 key가 달려 있는 것과 같다. thread가 인스턴스에 접근해서 수행하다가 synchronized method를 만나면 인스턴스에 있는 key를 thread가 지니고 메서드를 실행한다. 그러다 메서드 실행 중간에 thread scheduler에 의해 runnable로 내려올 때에 key를 가지고 있는 상태로 내려온다. 다음 thread가 run에 올라가 해당 메서드를 실행하러 가면 key가 없기 때문에 method에 진입하지 못한다. 이 상황에서 run에 있으면서 CPU를 점유하고 있으면 비효율적이기 때문에 synchronized pool에 들어가게 된다. 그리고 runnable에 있는 key를 가진 thread가 synchronized method를 벗어나는 event이벤트가 발생하면 key가 반납되는 이벤트가 발생하고 key가 반납되는 이벤트가 발생하면 synchronized pool에 있던 thread는 탈출하게 된다. synchronized keyword를 사용하면 key를 이용해 하나의 메서드에 하나의 thread만 접근해 수행을 마칠 수 있다.
(만일 하나의 상태를 메서드A와 메서드B가 건드리는 상황에서 스레드1은 A를, 스레드2는 B를 수행한다고 가정하면 하나의 상태에 대해 동기화 문제가 발생한다. 따라서 synchronized 메서드가 따로 실행되는 것을 막기 위해 key는 인스턴스가 가지고 있는다. 하나의 인스턴스 안에 synchronized 메서드는 두 개가 되는 것이다. 하나의 synchronized 메서드를 수행하려면 key를 가지고 있어야 하고 두 번째 스레드가 올라와서 다른 synchronized 메서드를 수행하려 해도 key가 없기 때문에 수행할 수 없다.)

synchronized modifier를 이용해도 multi-Thread의 문제점을 완전히 해결한 것은 아니며, 부하over head와 dead lock이라는 문제가 발생한다. 
부하 over head는 synchronized modifier를 메서드에 붙였을 때 동기화문제가 발생하지 않는 코드들까지 다른 스레드가 작업할 수 없게 해 효율적이지 못한 상태를 말한다. 해당 메서드의 동기화 문제가 발생하는 부분만 key를 가지고 있게 한다면 동기화 문제가 발생하지 않으면서 효율적이게 작업할 수 있다. 즉 key를 가지고 있어야 하는 범위를 메서드 전체가 아니라 일부분으로 줄이는 것이다. 
그 방법이 synchronized block을 사용하는 것이다. 메서드 안에서 동기화 문제가 발생할 수 있는 범위를 synchronized 블럭으로 묶어, synchronized 된 블럭을 만나기 전까지나 synchronize 블럭을 수행하고 난 다음은 key가 없어도 작업이 가능하다. synchronize block을 사용할 때에는 인스턴스의 key를 가져오라고 전달 해줘야 한다. synchronize(this){} 여기서 this는 이 블럭이 속해있는 메서드를 호출한 인스턴스를 가리킨다.

dead lock은 시스템이 죽은 것처럼, 작동하지 않는 것이는 것이다. 여기서는 키를 가지고 무한반복을 도는 경우를 말한다. 키를 가지고 무한반복 도는 스레드가 있다면 그 부분은 다른 스레드가 영원히 작업할 수 없다. 키를 반납하지 않기 때문이다. dead lock은 synchronized modifier를 사용할 때 뿐 아니라 IO Block에서도 발생한다. 이 때에도 IO Block pool에 들어가게 된다. dead lock은 특별한 해결법은 없으며 코딩을 잘 해야 한다. 

<<<예외 발생 활용법>>>
예외가 발생하면 하부 실행문을 실행하지 않는 것을 이용해 메서드를 조작할 수 있다.
//thread run() method
  //==> TheaterReservationSystem에서 getTicket()은 표가 없으면 Exception을 발생시킨다.
  public void run(){
    try{
  if(trs.getTicket(this)){
  System.out.println("\t\t<지점System>"+shopName+ " : 1번째 예매시도 및 완료");
  }
  if(trs.getTicket(this)){
  System.out.println("\t\t<지점System>"+shopName+ " : 2번째 예매시도 및 완료");
  }
  if(trs.getTicket(this)){
  System.out.println("\t\t<지점System>"+shopName+ " : 3번째 예매시도 및 완료");
  }
    }catch(Exception e){
      System.out.println("\t\t<지점System>"+shopName+ "표가 없네요.");
    }
  }
예외가 발생하지 않으면 첫 if문에서 표가 없음을 확인하고도 나머지 if문을 다 돌아야 한다. 그러나 첫 번째 if문에서 예외가 발생해 돌아오면 하부 구조는 실행하지 않고 catch로 넘어가기 때문에 효율적으로 작업할 수 있다.

<<<IO Block Pool>>
IOBlock은 IO를 열고 read()할 게 없으면 다음 수행문으로 넘어가지 못하고 멈춘다. 이 때 CPU를 계속 점유하고 있으면 비효율적이기 때문에 해당 thread스레드는 IOBlock pool에 들어간다. 그리고 데이터가 입력되는 event 발생 시 Runnable로 탈출한다. 

<<<bin>>>
실행파일이 들어있음

<<<class diagram>>>
클래스 UML을 보고 관계 모델링을 파악할 수 있다. Generalization, Realization 등.