Synchronized Block, Synchronized method



동기화(Synchronized)


- 공유 데이터(나머지 synchronized 된부분) 에 lock을 걸어 작업중이던 쓰레드가 마칠때까지 다른 쓰레드에게 제어권이 넘어가지 않게 보호한다.

- 교착상태에 주의해야 한다.



쓰레드는 같은 프로세스 내 데이터를 공유하기 때문에 일관성에 영향을 끼칠 수 있다. 

만약 공유하는 자원을 처리하던 중 다른 쓰레드가 접근하기라도 한다면 ? 


즉 동기화를 통해, 위와 같은 상황을 방지하고 어느 한 시점에 하나의 쓰레드만 접근하여 자원을 선점하고 프로그래머는 이를 효율적으로 관리할 필요가 있다. 


예를 들어, 작업 중인 문서에 누군가 들어와 편집을 해서 내가 했던 자료가 날라갔다고 가정하면 큰 낭패 일 것이다. 작업중인 문서에 lock을 걸어 다른 사람의 편집권한을 막는 것과 같은 이치라고 생각하면 된다.


ex) Singleton Pattern의 인스턴스를 여러 객체가 접근 할 때, Static으로 정의된 객체를 여러 객체가 접근 할 때, 하나의 객체에 여러 개의 쓰레드가 접근하려고 할 때.. 




 





사용법


(1) 특정 객체 인스턴스에 lock을 걸려고 할 때 - synchronized block

private Worker wokrer = new Worker();


public void work(){

synchronized(worker){


//input


}


}



(2) 메소드에 lock을 걸 때 - synchronized method

public synchronized work(){


//input code

Worker worker = new Worker();

}



특정 인스턴스의 사용을 동기화 할 것이냐?  혹은 메소드 자체를 동기화 할 것이냐?의 구분의 차이로 인식하면 쉬울 것 같다. 

사소하지만 주의깊게 생각해야 할 점이 있다면, (1) 번의 경우 synchronized 된 부분이 임계 영역이라면, (2) 번의 경우 메소드 전체가 임계영역이 된다는 점이다.





Sample Code


class Worker { private Object obj; private int count=0; public Worker() { } public synchronized void writeData(Object obj) { this.obj = obj; count++; } public Object getObj() { return obj; } public void setObj(Object obj) { this.obj = obj; } @Override public synchronized String toString() { return "Worker [obj=" + obj + ", count=" + count + "]"; } } public class Main { public static void main(String[] args) {

//Worker라는 하나의 인스턴스를 공유할 것이다. Worker worker = new Worker(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub while (true) { try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } worker.writeData("Writer1"); System.out.println("Data Print :"+worker.toString()); } } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub while (true) { try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } worker.writeData("Writer2"); System.out.println("Data Print :"+worker.toString()); } } }); thread1.start(); thread2.start(); } }



- WriteData 메소드와 toString 메소드를 동기화함에 따라 순차적인 Count 증가 및 Data를 Write할 수 있었다.





=>결과 출력







위의 방법처럼 synchronized 된 메소드를 구성했다면 , 객체를 synchronized 하여 구성하는 것도 쉽게 할 수 있을 것이다.

















activeMQ를 살펴보기 전에 필요한 기본적인 용어




- 메시지 지향 미들웨어(Message Oriented Middleware : MOM) 

분산 시스템 간 메시지를 주고 받는 기능을 지원하는 소프트웨어나 하드웨어 인프라

 

- 메시지 큐(Message Queue : MQ)

: MOM을 구현한 시스템

 

- 브로커(Broker)

: Message Queue 시스템

 

- AMQP(Advanced Message Queueing Protocol)

: 메시지 지향 미들웨어를 위한 프로토콜


-JMS(Java Message Service)

: 자바 메시지 서비스(Java Message Service; JMS)는 자바 프로그램이 네트워크를 통해 데이터를 송수신하는 자바 API이다.








=> activeMQ는 JMS로 구현한 메시지 지향 미들웨어(MOM)이다.






activeMQ 버전별 Java 최소 기준


 version

Minimum Java 

 ActiveMQ 4.x

 Java 5

 ActiveMQ 5.0 - 5.7

 Java 5 

 ActiveMQ 5.8 - 5.10

 Java 6 

 ActiveMQ 5.11

 Java 7 

 ActiveMQ 5.15.0

 Java 8  




로그란?


log. 컴퓨터 혹은 시스템 상태를 관찰할 수 있도록 에플리케이션이 제공하는 정보. 


- 로그를 통해 프로그램의 특정 상황이나 발생되는 이벤트의 정보를 얻을 수 있다.

- 로그를 통해 개발자는 시스템의 현재 상황에 대한 정보를 얻을 수 있고 향후 시스템을 개선하기 위해 노력하여야 한다.


 즉 프로그램과 개발자가 소통할 수 있는 창구라고 생각한다.







로그 레벨


 - log4j 라이브러리는 자주 활용되며 OFF, FATAL, ERROR, WARN, INFO, DEBUG, TRACE 로 나눌 수 있다.

OFF       :  로그 사용 해제
FATAL    :  시스템 혹은 애플리케이션의 종료를 유발 할 수 있는 아주 심각한 오류가 발생한 상태 
ERROR   :  특정 이벤트나 상황에서 오류가 발생한 상태
WARN    :  앞으로 오류를 발생할 만한 요소에 대한 경고를 나타냄
INFO      :  시스템 시작 또는 종료 등 상태 변경에 대한 정보를 제공하는 단계

DEBUG   :   디버그. 시스템 흐름에 대한 정보를 제공할 때 사용

TRACE    :  DEBUG 단계보다 더 자세한 정보를 제공할 때 사용

'programming > Exception' 카테고리의 다른 글

# 예외처리 (1) - 예외처리의 중요성  (0) 2018.02.24


예외처리에 대한 생각



프로그램에서 예외는 발생할 수 밖에 없고, 특히 예외로 인해 서버 프로그램이 작동을 멈춘다면 문제가 발생할 요소가 있다.  적어도 발생한 예외가 어떤 예외이며, 이것이 무시해도 될 수준인지 아니면 반드시 처리를 해야하는 것인지 그것도 아니라면 시스템 자체를 종료시켜야하는지에 대해 생각해봐야한다.

시스템에서 훌륭한 기능보다 중요한 것이 손쉬운 보완이라고 생각한다. 예외는 프로그램의 보완에 있어 첫걸음이 될 것이다.

첫 단추를 잘 꿰 메야 한다는 말이 있듯이, 프로그램을 설계하고 구축하는 첫 단계에서 수많은 예외상황에 생각하고자 스스로 노력하자.


 
1. 에러와 예외는 어떻게 다른가? 

error : 시스템 단계에서 발생. 시스템의 비정상적인 상황이므로 예외처리가 아닌 시스템 환경을 개선해야 한다.
Exception : 프로그램 로직에서 발생. 프로그래머가 작성한 로직에서 예외를 예상하여 구분하고 처리해야 한다.


2. 예외의 구분 

                                    예측가능한 예외 vs 예측 불가능한 예외

* 예측 가능한 예외 
 - 프로그램에서 당연히 발생 할 수 밖에 없는 상황.    ex) 로그인 실패, 데이터 조회 실패 .. 등

* 예측 불가능한 예외
 - 에러와 같은 수준의 레벨.     ex) 버그, 시스템의 메모리 문제 .. 등
 - 시스템 환경에서 개선해야 한다. 

 

  
                                     Runtime Exception vs 그 밖의 예외

* Runtime Exception (unchecked Exception)

 - 실행 단계에서 확인합니다.
 - 그렇기 때문에 처리를 강제하지 않습니다.
 - ex) nullpointerException , IndexOutOfBoundsException, .. 등
 - 하지만 이 또한 버그이기 때문에 반드시 인지하고 처리해야 할 의무가 있습니다.



* 그 밖의 예외 (checked Exception)
  - 컴파일 단계에서 확인합니다.
  - 예외에 대한 처리를 강제합니다.
  - ex) IOException .. 
  



3. 예외를 잡은 이 후의 행동



(1) 예외를 잡았다면 반드시 처리해야 한다.

  - 하지만 그 예외가 에러라면? 저라면 시스템 환경을 개선하고 처리하지 않겠습니다.

  - 시스템을 종료하거나 혹은 예외에 대한 상황을 처리 할 수 있다.



(2) 로그를 반드시 남겨야 한다.

 - 예외가 어떤 예외인지 구체적으로 명료하게 로그로 남겨야 한다.  

 - 예외가 발생한 원인, 시스템의 정보, 이외 반드시 판단 가능한 메세지.

 - 로그는 프로그래머가 처리 상황을 판단 할 수 있는 구체적인 증거입니다. 특히 서버 개발자에겐..

  


(3) 새로운 예외를 던질 수 있다.

 - 세세한 예외를 더 추상화하여 던진 경우이다.  

 - 예외 원인에 대한 정보가 부족할 경우 새로운 예외를 던짐으로써, 구체적인 정보를 얻을 수 있다.

 


(4) 예외 무시

 - 큰 이상이 없다고 판단되면 예외를 무시할 수 있다.

 - 좋은 방법은 아니다.





4. 예외처리 패턴



(1) 예외복구

try{ .... } catch(SQLException e) {

logger.error("Insert Query Failed . Method Name '' , User id is blar...");

e.printstackTrace();

}finnally{

..

예외에 대한 처리작업을 수행해야 할 것이다.

doAnything();

}

 => 예외복구에 대해 반드시 인지해야 되는 점은 프로그램이 정상적으로 작동하게끔 수행해야 한다는 것이다. 비록 예외가 발생되었어도 프로그램 로직에 의해 시스템이 종료 혹은 계속해서 수행할 수 있어야 할 것이다.




(2) 예외회피

public void process() throws Exception {

......

}



  => 예외를 메소드에 정의함으로써, 예외에 대한 처리를 수행하지 않고 있다. 또한 추상클래스인 Exception을 예외로 던졌기 때문에, 구체적인 예외에 대한 처리가 부족하다. 즉, 좀 더 세부적인 예외를 잡아먹고 있다. 예외회피를 불가피하게 해야할 경우를 제외한다면, 신중하게 선택해야 할 것이다.

 



(3) 예외전환

try{ .... } catch(Exception e) {

... 추상 Exception에 대한 정보 또한 로그로 남기면 좋을 것이다..

throws New SQLException("Exception is SQLException. Insert Query Failed . Method Name '' , User id is blar...");

}

 => 예외를 중첩하여 사용하여 어떤 예외에 대한 처리인지 분명하게 처리하였다. 만일 복구가 불가능 한 예외에 대한 처리라면 RuntimeException으로 전환하여 다른 예외에 대해 일일히 처리하지 않게끔 할 수 있다. 










예외에 대해 정리하면서 느낀 생각은, 개발자라면 항상 예외에 대해 염두하고 있어야 한다는 점입니다.  (특히 나같은 초급개발자라면..)


예외를 잡았다면 반드시 처리해야 할 것이며, 예외 상황에 대한 충분한 로그를 남겨야 할 것입니다. 충분히 예외상황에 대해 인지하고 처리를 했다면 유지보수 측면에서 혹은 협업단계에서 효율적으로 시스템을 개선할 수 있을 것입니다.


 

=> 예외의 목적은 시스템 개선이다!!










참고사이트 : https://www.slideshare.net/dhrim/ss-2804901

'programming > Exception' 카테고리의 다른 글

#예외처리 (2) - log level  (0) 2018.02.24

+ Recent posts