2014.08.14 13:28

※원래 쓰레드의 개념과 사용법에 대해 처음 공부하시는 분들을 위해 굉장히 상세히 포스팅 할 예정이었으나 역시 단순한 포스트에 모든 내용을 실을 수 있을 정도로 세상은 만만치 않았습니다.ㅡㅡ;;; 그림 한장 없이 텍스트로만 , 그것도 상세히가 아닌 띄엄띄엄 포스팅하게 되어서 안타깝습니다. 너무 판을 벌려서 정리가 안되는군요;;;;

1. 쓰레드의 의미
쓰레드(Thread)란 무엇인가?
아직 쓰레드 프로그래밍을 해보지 않은 사람이라면 가끔 이런 생각을 해봤을 것이다. '프로그램을 만들 때 하나의 프로그램에서 동시에 두가지 이상의 작업이 이루어지게 하려면 어떻게 해야할까?' 

예를들어 "음악이 흐르는 포토뷰어" 라는 프로그램을 생각해보자. 사진을 감상하는 프로그램인데 사진만 감상하긴 좀 밋밋해서 배경으로 음악을 깔고 싶어졌다. 사진을 보면서 음악을 듣는 것에 그치는게 아니라 사진을 넘기고 음악을 바꾸는 작업이 동시에 이루어져야 한다면? 

분명 지금까지의 만들어본 프로그램은 main 함수라는 하나의 흐름에 의해 모든것이 이루어 졌을 것이다. 하지만 이젠 사진도 흘러가야하고 음악도 흘러가야 하게 된 것이다. 이렇게 하나의 프로그램 안에 둘 이상의 흐름을 가진 프로그램을 만들 때 우리는 쓰레드를 이용한다.

2. 쓰레드와 멀티프로세스의 차이점
둘 다 두가지 이상의 작업을 동시에 처리할 수 있는 매커니즘이다. 하지만 분명 차이가 있다. 

우선 프로세스(process)에는 프로세스를 이루는 몇가지 영역이 있는데 보통 Code영역,Data영역,Heap영역,Stack영역으로 구성된다. 이것들이 모여 하나의 프로세스가 된다. 멀티프로세스가 이 같은 프로세스를 통째로 여러개 수행시키는 것이라 한다면 쓰레드는 하나의 프로세스 옆에 붙어 기생(ㅡㅡ;) 하며 수행되는 놈 쯤이 되겠다. 

쓰레드는 프로세스가 가진 Code,Data,Heap 영역을 프로세스와 공유한다. 오직 Stack만 따로 가지고 있는 놈을 쓰레드라고 부른다. 아무래도 공유하는 영역을 가지다보니 하나의 프로세스에 붙어있는 여러 쓰레드간, 혹은 프로세스와의 통신에 별다른 부담이 없다. 멀티프로세스에서는 프로세스간에 통신을 하기 위해서 IPC를 통해서 통신이 가능하다. 그리고 프로세스를 통째로 여러개 돌리면 쓰레드보다 시스템의 부하가 많이 걸릴것이다. 

여튼 우리는 쓰레드가 프로세스의 Stack영역을 제외한 나머지 영역을 공유한다는 사실 정도만 알고가자.

3. Lock과 Monitor에 대하여
쓰레드는 위에서 말했듯이 동시에 2가지 이상의 흐름을 갖는 것을 의미한다. 하지만 문제가 되는경우가 몇가지 있다. 그중 하나가 자원공유에 관한 것이다. 

만약 A라는 쓰레드가 money라는 변수의 값을 변경한다고 하자. money가 100원이라는 값을 가지고 있는데 A가 여기에 50원이라는 값을 넣으려고 한다. 우선 'money + 50원'을 해서 150원 이라는 값을 얻었다. 이제 money에 150원이라는 값을 대입하기만 하면 된다. 근데 그때 B라는 쓰레드가 money에 100원을 더 넣은 것이다. B는 200원이 되었을 거라 생각했다. 물론 200원이 되었다. 근데 그 바로 직후에 A쓰레드가 150원이라는 값을 money에 대입한것이다. A는 당연히 150원이 있는걸로 알고 있지만 B는 분명 200원이어야 할 money가 150원으로 둔갑한 것에 대해 기겁하게 되는 것이다.

그렇다. 두개의 쓰레드가 동시에 도는것은 좋은데 하나의 자원(money)을 무분별하게 사용하다보니 이런일이 벌어진 것이다. 
Lock과 Monitor란 이런 문제를 해결하기 위해 등장한 것이다. Monitor는 하나의 쓰레드가 하나의 객체에 대한 락을 획득했을 때 다른 쓰레드가 그 객체에 접근하여 동기화 시킨 영역에 접근하는 행위를 막아준다.

일단 이렇게 일이 처리되면 위의 예에서 A가 먼저 money를 사용중이기에 B는 money에 100원을 추가할 수가 없다. A가 작업이 끝나고 150원이 된 후에 B가 100원을 추가해 결국 250원을 만들 수 있는것이다.

이런 Lock을 mutual exclusion lock, 뮤텍스락이라고도 부르며 한국말로는 상호배제락이라 부른다. 그리고 이러한 락에 의해 쓰레드와 공유자원사이의 상호작용을 제어하는 행위를 동기화(synchronization)라고 부른다.

4. Monitor
Java의 오브젝트에는 Monitor가 할당된다. 이 모니터는 자신을 소유한 쓰레드를 제외한 다른 쓰레드의 접근을 막는다. 이러한 특성을  mutual exclusion 혹은 뮤텍스라고 부른다. 이렇게 모니터는 일시적으로 다른 쓰레드가 외부에서의 접근을 포기하게 만드는 기능을 하는데 만약 쓰레드가 어떤 오브젝트의 락을 획득하면 그 락을 획득한 동기화 영역에는 다른 쓰레드의 접근이 불가하다. 다른 쓰레드가 이 오프젝트의 락을 획득하기 위해서는 이전 쓰레드가 잡고있는 락을 풀어줘야지만 락의 획득이 가능하다. 이는 wait같은 대기모드 함수를 호출하거나 동기화 블록이 끝나는 부분에서 락을 자동으로 해제하게 된다.
< JAVA Whitre Paper 문서에서 발췌 - The most basic of these methods is synchronization, which is implemented usingmonitors. Each object in Java is associated with a monitor, which a thread can lock or unlock. Only one thread at a time may hold a lock on a monitor. The synchronized statement computes a reference to an object; it then attempts to perform a lock action on that object's monitor and does not proceed further until the lock action has successfully completed.>

5. 데드락
여러 회사의 면접 질문으로 굉장히 많이 나오는 데드락.
A라는 쓰레드가 특정 객체의 락을 얻게되면 그에 따른 자원을 block 하게 된다. 근데 그 쓰레드가 자신의 작업을 처리하기 위해 외부의 자원이 필요로 하는 경우가 생기게 된다. 다행이 그 자원이 누구나 접근 가능한 상태라면 불러와서 사용할 수 있지만 만약 B라는 쓰레드가 그 자원에 락을 걸고 있다면 A쓰레드는 B가 락을 해제 할 때 까지 기다리는 수밖에 없을 것이다. 설상가상으로 B 또한 A가 block하고 있는 자원을 원하고 있다면 A와 B는 서로가 Lock을 풀때까지 무한정 기다리게 되는데 이러한 상태를 데드락(Dead Lock)이라고 부른다.
이 데드락을 해결하는 방법은 데드락이 발생하기 않게 프로그래밍 하는 방법이 가장 좋다고 밖에 할 수 없다. 그외에 쓰레드의 우선순위에 따라 하나의 쓰레드를 포기하는 방법이 있는데 이러한 해결책은 프로그램의 성능저하의 큰 원인이 될것이다.
데드락이 발생하는 코드를 짜보는 것도 좋을듯...(왜!!;;)

6. 동기화(synchronized)
- 함수 이름 앞에 쓰이는 synchronized는 synchronized(this)와 동일함
- synchronized(instance) 와 synchronized(Class)와의 차이 
   => http://pupustory.tistory.com/149
- 블록으로 동기화를 사용할 때 synchronized()의 인자로 들어가는 객체는 그 객체를 Lock에 의해 보호하겠다는 말씀
- synchronized의 블록의 의미는 그 블록의 코드가 끝날때 까지 인자로 받은 객체의 Lock을 풀지 않겠다는 의미
- A라는 쓰레드가 Lock을 건 객체를 B라는 쓰레드가 그 객체의 동기화 블록외의 코드를 접근 가능하다. 즉 Lock을 건다는 말은 그 객체의 전체에 락을 거는것이 아니라 동기화 영역에 대해서만 락이 걸린다는 의미
- 꼭 Lock이 걸려있는 객체가 어떤일을 해야만 하는것은 아니다. 마치 동기화를 위한 변수처럼 사용이 가능하다.
   => http://iilii.egloos.com/4071694

7. wait/notify (작업순서)
하나의 synchronized된 자원을 두개이상의 쓰레드가 사용할 때는 하나의 쓰레드가 모든 작업이 끝난 이후에 다음 쓰레드가 작업을 시작하는 것이 아니라 때론 블록된 단위별로 번갈아 가면서 사용해야 할경우가 있다. 이럴때 현재의 쓰레드의 락을 잠깐 해제하고 대기 중이던 쓰레드를 깨워서 사용하는 과정이 필요한데 이러한 과정을 가능하게 하는 함수가 wait() 과 notify() 함수이다.

- wait()에 걸리면 그 코드를 수행하고 있던 쓰레드를 휴지상태(sleep)상태로 만들어줌. 그자리에서.
- wait()과 동시에 쓰레드가 대기상태로 돌아가면서 인자로 받은 객체에 걸고있던 Lock을 해제하게 됨
- 다른 쓰레드가 위의 객체에 Lock을 가지게 되고 작업이 끝나면서 notify()호출해서 휴지상태로 대기하고 있던 쓰레드를 깨워줌
- 단, notify()가 호출된다고 바로 대기 쓰레드가 시작되는게 아니라 지금 수행되던 쓰레드의 synchronized 블록이 끝나야지 대기중이던 쓰레드가 실행된다.
- 대기 쓰레드가 실행될 때는 그 쓰레드가 다시 객체에 대한 Lock을 가지며 wait() 코드 다음 코드부터 시작된다.

그밖에 쓰레드의 상태를 관리하는 함수들이 있다. 그 쓰레드의 상태와 이를 관리하는 함수는 다음의 링크에서 확인 할 수 있다.
=> http://www.javastudy.co.kr/docs/jm/jm15.html
=> http://www.javastudy.co.kr/docs/lec_java/thread/t2.html

8. 쓰레드 사용방법
- 아.. 결국 포스팅이 귀찮아져 이렇게 긁어 오기를 해버리는구나.. ;;;;
   => http://signpen.net/blog/106 
   => http://blog.daum.net/bifrost0076/5882414

add) 세마포어
 .. A사의 2차 면접에 나왔던 질문. OS질문 하더니 갑자기 세마포어에 대해 묻더라.. 모니터와 유사한 개념이지만 조금은 다르다. 카운터를 설정할 수 있는 세마포어도 있고 .. 조금 다르긴한데 시간을 두고 포스팅하자.. 쓰레드 포스팅을 정리하는데 너무 많은 시간과 노력을 투자해버려서 이젠 보기도 싫다.



참조  : http://openparadigm.tistory.com/30

저작자 표시
신고

'좋은 설계자 & 좋은관리자 & 좋은개발자' 카테고리의 다른 글

쓰레드 정리.  (0) 2014.08.14
뮤택스 세마포어  (0) 2014.08.14
Posted by hoonihoon85 hoonihoon
2014.08.14 10:47

1. 뮤텍스 기본 정의 


뮤텍스는 한 번에 하나의 쓰레드만이 실행되도록 하는 재 입장할 수 있는 코드 섹션에 직렬화된  접근이 가능하게 할 때 사용됩니다. 뮤텍스 객체는 제어되는 섹션에 하나의 쓰레드만을 허용하기 때문에 해당 섹션에 접근하려는 다른 쓰레드들을 강제적으로 막음으로써 첫 번째 쓰레드가 해당 섹션을 빠져나올 때까지 기다리도록 합니다.


뮤텍스는 화장실에 들어가기 위한 열쇠로 비유할 수 있습니다. 즉 화장실에 들어갈 수 있는 열쇠를 한 사람이 갖고 있다면, 한 번에 열쇠를 갖고 있는 그 한 사람만이 들어갈 수 있습니다. 화장실에 열쇠를 갖고 있는 사람이 들어가 볼일을 다 본 후에는 줄을 서서 기다리고 있는(대기열-큐) 다음 사람에게 열쇠를 주게 됩니다.  



세마포어는 공유 리소스에 접근할 수 있는 최대 허용치만큼 동시에 사용자 접근을 할 수 있게 합니다. 쓰레드들은 리소스 접근을 요청할 수 있고 세마포어에서는 카운트가 하나씩 줄어들게 되며 리소스 사용을 마쳤다는 신호를 보내면 세마포어 카운트가 하나 늘어나게 됩니다.


뮤텍스는 값이 1인 세마포어입니다.

 

세마포어는 빈 화장실 열쇠의 갯수라고 보면 됩니다. 즉, 네 개의 화장실에 자물쇠와 열쇠가 있다고 한다면 세마포어는 열쇠의 갯수를 계산하고 시작할 때 4의 값을 갖습니다. 이 때는 이용할 수 있는 화장실 수가 동등하게 됩니다. 이제 화장실에 사람이 들어갈 때마다 숫자는 줄어들게 됩니다. 4개의 화장실에 사람들이 모두 들어가게 되면 남은 열쇠가 없게 되기 때문에 세마포어 카운트가 0이 됩니다. 이제 다시 한 사람이 화장실에서 볼일을 다 보고 나온다면 세마포어의 카운트는 1이 증가됩니다. 따라서 열쇠 하나가 사용가능하기 때문에 줄을 서서 기다리고 있는 다음 사람이 화장실에 입장할 수 있게 됩니다.


2. 언제 사용하나요?


Process 혹은 Thread 간의 communication / 통신 시에 shared memory (공유 메모리) 등을 쓰는 경우에 하나의 자원 (Resource or memory or even files) 에 두 개 이상의 process or thread 가 write 을 하는 경우에 문제가 발생한다. 이를 방지하기 위해 하나의 process 혹은 thread 만이 resource 에 접근하여 write 을 할 수 있도록 제어해 주어야 하고, 이 때에 Mutex 혹은 Semaphore 를 사용한다. Thread 에서는 MuTex 를 사용하고, Process 에서는 Semaphore 를 사용한다.




3. 뮤텍스 상세

공유 자원에 대한 접근 제어 

다수의 객체가 공유 자원에 접근하려고 하면, (공유 자원의 종류에 따라서)접근 시점을 동기화 시켜줄 필요가 생긴다. 여기에서 동기화란 시간과 공간을 맞추어 준다는 의미로, 즉 공유 자원 영역 (공간)에 접근하는 객체들의 진입 시간을 제어할 수 있어야 함을 의미한다.

멀티 Thread(쓰레드) 프로그램 역시 공유 자원에 여러 개의 쓰레드가 접근할 수 있으므로 공유 자원 영역에 대한 동기화가 필요 하다.

카운팅 프로그램을 예를 들어 보자. 카운트 변수는 전역:::변수(:12)로 A,B 두개의 쓰레드가 공유하면서, 1씩 증가하는 카운팅 정보를 유지하기 위해 사용된다. 공유 자원 영역 즉 "count 값을 읽어 오고, 연산을 해서 저장하는" 영역에 대한 쓰레드간 동기화가 이루어지지 않는 다면 아래와 같은 일이 발생할 수 있을 것이다.
  1. global int count = 0
  2. A 쓰레드가 count값 0을 읽는다.
  3. B 쓰레드가 count값 0을 읽는다.
    • A 쓰레드가 카운팅 연산을 하기 전에 B쓰레드가 접근해 버린 상황이다.
  4. A 쓰레드가 count+1 연산을 하고 값을 쓴다. count = 1
  5. B 쓰레드가 count+1 연산을 하고 값을 쓴다. count = 1
A와 B쓰레드가 한번씩 카운팅 연산을 했으므로, count값은 2가 되어야 하겠지만, 실제로는 1이 저장되어 버렸다.

이러한 문제의 해결을 위해서 쓰레드를 동기화 시켜줄 필요가 있다..


일반적으로 동기화는 "공간과 시간"을 제어하는 방식으로 이루어진다. 즉 "접근 제어가 필요한 공간"을 지정하고 지정한 "공간에 진입 할 수 있는 시간"을 제어하는 방식이다.


여기에서 "접근 제어가 필요한 공간"에는 보호 해야 할 공유 자원이 놓인다. 보호 해야할 공유 자원이 있는 공간을 임계 영역이라고 한다. 시간 제어는 해당 임계 영역에 동 시간에 단지 하나의 쓰레드만 접근하도록 제한 하는 방식으로 이루어진다.


임계영역에 들어가기 위한 하나의 키를 가지고 경쟁하는 것으로 이해하면 된다. 임계영역에 들어가기 위한 키는 단지 하나 밖에 없으므로 어떤 스레드가 키를 얻어서 임계영역에 진입하면, 다른 스레드는 키를 얻을 때까지 - 앞서 임계영역에 진입한 프로세스가 키를 되돌려줄 때까지 - 기다려야 한다.


 

위의 카운팅 프로그램을 예로 설명해보자. 임계 영역은
  1. count 값을 읽어와서
  2. 카운팅 연산을 하고
  3. 연산 결과를 저장하는
코드 영역으로 지정할 수 있을 것이다.


이 임계 영역에는 오직 하나의 쓰레드만이 진입할 수 있다. 즉 쓰레드 A가 임계 영역에 진입해서 코드를 수행중에 있다면 쓰레드 B는 임계 영역 밖에서 기다려야 한다. 이렇게 쓰레드를 동기화 시킴으로써, 아래와 같이 제대로된 카운팅 연산을 보장할 수 있게 된다.
  1. global int count = 0;
  2. A 쓰레드가 임계 영역에 진입
  3. B 쓰레드도 임계 영역 진입을 시도하지만, A 쓰레드가 진입해 있으므로 임계 영역 밖에서 대기한다.
  4. A 쓰레드가 count 값을 읽고
  5. 카운팅 연산을 해서
  6. 값을 저장한다. count = 1
  7. A 쓰레드가 임계 영역에서 빠져나온다.
  8. 비로서 B 쓰레드가 임계 영역에 진입해서
  9. count 값 1을 읽어서 카운팅 연산을 하고 저장한다. count = 2
제대로 카운팅 연산이 되는걸 확인할 수 있다.

Mutex 

뮤텍스는 pthread에서 제공하는 동기화 매커니즘으로 공유 자원 공간에 대한 접근 시간 제어로 동기화를 달성한다. 기본적인 매커니즘은 세마포어(:12)와 비슷하다. 특히 POSIX(:12) 세마포어와 비슷하며, 동기화 매커니즘으로 뮤텍스 대신 세마포어를 사용할 수도 있다. 동기화 매커니즘의 핵심은 상호 배제로 다음과 같이 달성 한다.

global int v = 1;
lock()
{
    while(1)
    {
        if (v==1)
            break;
    }
    v = 0;
    return 1;
}

unlock()
{
    v = 1;
    break;
}
어디까지나 매커니즘 상으로 그렇다는 얘기고, 세마포어와 마찬가지로 busy wait 상태에 놓이지 않음을 보장한다.

상호 배제는 잠금 형식으로 이루어진다. 쓰레드는 잠금 v를 얻어야 임계 영역에 진입할 수 있다. 임계 영역을 빠져나오면 잠금을 되돌려 줘서 다른 쓰레드가 잠금을 얻을 수 있도록 한다.

뮤텍스 메커니즘의 특징을 정리했다.

  • Atomicity - mutex 잠금(lock)는 최소단위 연적(atomic operation) 으로 작동한다. 이말의 뜻은 하나의 쓰레드가 mutex 를 이용해서 잠금을 시도하는 도중에 다른 쓰레드가 mutex 잠금을 할수없도록 해준다는 뜻이다. 한번에 하나의 mutex 잠금을 하도록 보증해준다.
  • Singularity - 만약 스레드가 mutex 잠금을 했다면, 잠금을 한 쓰레드(:12)가 mutex 잠금을 해제 하기 전까지 다른 어떠한 쓰레드도 mutex 잠금을 할수 없도록 보증해준다.
  • Non-Busy Wait - 바쁜대기 상태에 놓이지 않는다는 뜻으로, 하나의 쓰레드가 mutex 잠금을 시도하는데 이미 다른 쓰레드가 mutex 잠금을 사용하고 있다면 이쓰레드는 다른 쓰레드가 락을 해제하기전까지 해당 지점에 머물러 있으며 이동안은 어떠한 CPU 자원도 소비하지 않는다 (이를테면 sleep). 

뮤텍스 만들기 

뮤텍스를 생성하기 위해서 우리는 먼저, 뮤텍스정보를 저장하기 위한 타입인 pthread_mutex_t 를 선언해주고 이것을 초기화 해주어야 한다. 선언과 초기화의 가장간단한 방법은 PTHREAD_MUTEX_INITIALIZER 상수를 할당하는 것으로 아래와 같이 사용할수 있다.
pthread_mutex_t a_mutex = PTHREAD_MUTEX_INITIALIZER;

혹은 pthread_mutex_init(3)함수로 뮤텍스를 생성할 수도 있다.
#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutex_attr *attr);
;;;


뮤텍스 잠금, 잠금해제, 제거 

뮤텍스 잠금을 위한 함수로는 pthread_mutex_lock() 함수를 제공한다. 이 함수는 해당 뮤텍스에 대해서 잠금을 시도하는데, 만약 잠그려는 뮤텍스가 다른 쓰레드에 의해서 이미 잠겨있다면, 잠금을 얻을수 있을때까지 - 이미 잠근 다른 쓰레드가 뮤텍스의 잠금을 해제할때까지 - 봉쇄(블럭)되게 된다. 다음은 이러한 뮤텍스 잠금을 얻기 위한 지원함수들이다.
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_destory(pthread_mutex_t *mutex);

pthread_mutex_trylock() 를 사용하면 잠금을 얻을수 없을경우 해당 코드에서 블럭되지 않고 바로 에러코드를 돌려준다. 즉 pthread_mutex_lock 의 비봉쇄 버젼이라고 생각하면 된다.

뮤텍스 잠금을 얻은후 해당 영역에서의 작업을 마친후 잠금을 해제하기 위해서 사용한다. 사용되는 함수는 pthread_mutex_unlock(3) 이며 함수원형은 다음과 같다.
int pthread_mutex_unlock(pthread_mutex_t *mutex);
다음은 쓰레드간 공유되는 자원을 위해서 잠금을 어떻게 사용하는지를 보여주는 간단한 예제다.

예제: mutex_lock.c
#include <stdio.h>  
#include <unistd.h>  
#include <pthread.h>  
 
int ncount;    // 쓰레드간 공유되는 자원 
pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER; // 쓰레드 초기화 
 
void* do_loop(void *data) 
{ 
    int i; 
    for (i = 0; i < 10; i++) 
    { 
        pthread_mutex_lock(&mutex); // 잠금을 생성한다. 
        printf("loop1 : %d\n", ncount); 
        ncount ++; 
        if(i == 10) return;
        pthread_mutex_unlock(&mutex); // 잠금을 해제한다. 
        sleep(1); 
    } 
} 
 
void* do_loop2(void *data) 
{ 
    int i; 
 
    // 잠금을 얻으려고 하지만 do_loop 에서 이미 잠금을  
    // 얻었음으로 잠금이 해제될때까지 기다린다.   
    for (i = 0; i < 10; i++) 
    { 
        pthread_mutex_lock(&mutex); // 잠금을 생성한다. 
        printf("loop2 : %d\n", ncount); 
        ncount ++; 
        pthread_mutex_unlock(&mutex); // 잠금을 해제한다. 
        sleep(2); 
    } 
}     
 
int main() 
{ 
    int       thr_id; 
    pthread_t p_thread[2]; 
    int status; 
    int a = 1; 
 
    ncount = 0; 
    thr_id = pthread_create(&p_thread[0], NULL, do_loop, (void *)&a); 
    sleep(1); 
    thr_id = pthread_create(&p_thread[1], NULL, do_loop2, (void *)&a); 
 
    pthread_join(p_thread[0], (void *) &status); 
    pthread_join(p_thread[1], (void *) &status); 
 
    status = pthread_mutex_destroy(&mutex); 
    printf("code  =  %d", status); 
    printf("programing is end"); 
    return 0; 
} 
위의 코드를 우선 mutex 잠금을 하지 않은체 컴파일후 실행해보자. 간단하게 pthread_mutext_lock 와 pthread_mutex_unlock 부만 주석처리하면 된다. 그러면 do_loop2 와 do_loop 가 일정한 간격을 두고 ncount 자원에 접근하는 것을 볼수 있을것이다. 그러나 우리는 do_loop 가 ncount 자원을 접근하고 있는동안 다른 쓰레드가 접근하지 않기를 원할때가 있을것이다. 이럴때 뮤텍스 잠금을 사용하면 된다.

위의 코드에서 뮤텍스 잠금 부분의 주석을 풀고 다시 컴파일해서 실행시켜보면, do_loop 쓰레드가 ncount 증가 작업을 모두 마칠때까지 do_loop2 쓰레드는 해당 영역에서 블럭됨을 알수 있을것이다. 이런식으로 하나의 쓰레드가 특정자원에 접근할때 다른 쓰레드가 접근하지 못하도록(한번에 하나의 쓰레드만 해당 자원에 접근할수 있도록) 제어할수 있다.

컴파일 방법은 gcc -o mutex_lock mutex_lock.c -lpthread 이다

더이상 뮤텍스를 사용할일이 없다면 pthread_mutex_destory 를 이용해서 뮤텍스 자원을 제거(free) 하도록 한다. 만일 뮤텍스자원을 사용하는 쓰레드가 하나라도 존재한다면 에러코드(EBUSY)를 리턴한다. 그러므로 모든 쓰레드의 뮤텍스에 대해서 pthread_mutex_unlock 을 이용해서 잠겨져야만 뮤텍스 제거가 성공할수 있다. 성공할경우 0을 넘겨준다.



4. java 상세 보기

“MUTual EXclusion 으로 상호배제라고도 한다. Critical Section을 가진 스레드들의 Runnig Time이 서로 겹치지 않게 각각 단독으로 실행되게 하는 기술이다. 다중 프로세스들의 공유 리소스에 대한 접근을 조율하기 위해 locking과 unlocking을 사용한다. 즉, 쉽게 말하면 뮤텍스 객체를 두 스레드가 동시에 사용할 수 없다는 의미이다.” (from reference #1)

“제어되는 섹션에 하나의 쓰레드만을 허용하기 때문에 해당 섹션에 접근하려는 다른 쓰레드들을 강제적으로 막음으로써 첫 번째 스레드가 해당 섹션을 빠져나올 떄 까지 기다린다. 예) Niclas Winquits씨가 2005년에 쓴 화장실 비유 뮤텍스는 화장실에 들어가기 위한 열쇠로 비유할 수 있다. 즉 화장실에 들어갈 수 있는 열쇠를 한 사람이 갖고 있다면 한번에 그 한 사람만 들어 갈 수 있다. 화장실에 들어간 사람이 나오면 줄을 서서 기다리는 다음 사람(대기열-큐)에게 열쇠를 주게 된다.” (from reference #2)

Mutex Implementation in Java (from reference #4)

public Class MyClass {
  private static Object lock = new Object();
  public void myMethod() {
    // Stuff that multiple threads can execute simultaneously.
    synchronized(MyClass.lock) {
      // Stuff that only one thread may execute at a time.
    }
  }
}

MyClass 의 instance 들은 lock 이라는 object 를 공유하고 (note ‘static’), shared resource 에 write 을 하기 이전에 test and set (lock 이 free 한지 test 해보고, free 하다면 lock 을 set 하고 critical section 에 진입, critical operation 을 마치고 free lock) 을 한다. Java 의 synchronized keyword 는 thread-safe 를 guarantee 한다. (후에 Java Synchronized keyword 관련 포스팅 요망)

Semaphore 란?

” 세마포어는 리소스의 상태를 나타내는 간단한 카운터로 생각할 수 있다. 일반적으로 비교적 긴 시간을 확보하는 리소스에 대해 이용한다. 유닉스 시스템의 프로그래밍에서 세마포어는 운영체제의 리소스를 경쟁적으로 사용하는 다중 프로세스에서 행동을 조정하거나 또는 동기화 시키는 기술이다. 세마포어는 운영체제 또는 커널의 한 지정된 저장장치 내 값으로서, 각 프로세스는 이를 확인하고 변경할 수 있다. 확인되는 세마포어의 값에 따라, 그 프로세스가 즉시 자원을 사용할 수 있거나, 또는 이미 다른 프로세스에 의해 사용 중이라는 사실을 알게 되면 재시도하기 전에 일정 시간을 기다려야만 한다. 세마포어는 이진수 (0 또는 1)를 사용하거나, 또는 추가적인 값을 가질 수도 있다. 세마포어를 사용하는 프로세스는 으레 그 값을 확인하고, 자원을 사용하는 동안에는 그 값을 변경함으로써 다른 세마포어 사용자들이 기다리도록 해야한다. ” (from reference #1)

” 공유 리소스에 접근할 수 있는 최대 허용치만큼 동시에 사용자 접근을 할 수 있게 한다. 쓰레드들은 리소스 접근 요청을 할 수 있고 세마포어에서는 카운트가 하나씩 줄어들게 되며 리소가 모두 사용 중 인경우(카운트 0) 다음 작업은 대기를 하게 된다. 리소스 사용을 마쳤다는 신호를 보내면 카운트가 하나 늘어나게 되고 다음 작업이 사용 할 수 있다. 예) 세마포어는 빈 화장실 열쇠의 갯수에 비유할 수 있다. 즉 비어있는 칸 만큼 열쇠가 있다고 가정하면 사람들이 화장실에 들어갈 때 마다 열쇠의 숫자는 줄어 들게 된다. 화장실 칸이 다 찰 경우 카운트는 0이 되며 다음 사람은 줄을 서서 기다린다. 볼일을 끝내고 나오면 리소스 사용을 마쳣다는 신호로 카운트를 하나 늘이고 다음 사람에게 부여 한다. ” (from reference #2)

Semaphore Implementation in Java (needs to be updated)

- Mutex 의 구현과 유사하다. Mutex 의 경우 0과 1의 두 상태 뿐인 lock 인 대신, Semaphore 의 경우 counter 여야 하고, lock 이 true 인지 확인하는 대신 counter 가 0 혹은 max # of threads allowed (구현 방식에 따라 min or max 를 취하는 방식) 인지를 확인하여, test and set 으로 critical section 에 진입하면 counter 를 1 증가 혹은 감소시키고, shared resource 에 write 하는 등의 작업이 끝난 뒤 critical section 을 exit 하면서 counter 를 되돌린다.

e) Mutex and Semaphore

- critical section 에서 (즉, mutex 의 lock 가지고 있거나 semaphore 의 counter 를 하나 소비 중인) processing time 이 오래 걸리는 경우는 어떻게 처리하나 -> time-out 을 설정하여 이를 관리한다. (추가 조사 + 포스팅 필요)

f) Mutex vs Semaphore (from reference #1)

  1. 세마포어는 뮤텍스가 될 수 있지만 뮤텍스는 세마포어가 될 수 없다. (mutex 는 상태가 0, 1 두 개 뿐인 binary semaphore 이다)
  2. 세마포어는 소유할 수 없는 반면 뮤텍스는 소유가 가능하며 소유주가 이에 대한 책임을 진다. (mutex 의 경우 상태가 두개 뿐인 lock 이므로 lock 을 ‘가질’ 수 있다.)
  3. 뮤텍스의 경우 뮤텍스를 소유하고 있는 쓰레드가 이 뮤텍스를 해제할 수 있다. 하지만 세마포어의 경우 이러한 세마포어를 소유하지 않는 스레드가 세마포어를 해제할 수 있다.
  4. 세마포어는 시스템 범위에 걸쳐있고 파일시스템상의 파일 형태로 존재한다. 반면 뮤텍스는 프로세스 범위를 가지며 프로세스가 종료될 때 자동으로 clean up된다.
  5. ★★★ 가장 큰 차이점은 관리하는 동기화 대상이 갯수이다. 뮤텍스는 동기화 대상이 오직 하나뿐일 때, 세마포어는 동기화 대상이 하나 이상일 때 사용한다.




정리참조: 
http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Thread/Beginning/Mutex

http://www.gpgstudy.com/forum/viewtopic.php?t=6198&highlight=%BC%BC%B8%B6%C6%F7%BE%EE

http://crack-tech-interview.com/2014/01/26/semaphore-%EC%99%80-mutex-mutual-exclusion/


저작자 표시
신고

'좋은 설계자 & 좋은관리자 & 좋은개발자' 카테고리의 다른 글

쓰레드 정리.  (0) 2014.08.14
뮤택스 세마포어  (0) 2014.08.14
Posted by hoonihoon85 hoonihoon
2014.06.24 17:45



메서드의 목적을 분명해야 된다. 너무길거나 메서드의 목적을 알 수 없이 여러개의 목적이 들어 간다면 리펙토링 하는 것이 좋다. 

메서드는 짧고, 이름이 명확해야 한다.


 
void printOwing() {
    Enumeration e = _orders.elements();
    double outstanding = 0.0;
 
    // 배너출력
    System.out.println ("**************************");
    System.out.println ("***** Customer Owes ******");
    System.out.println ("**************************");
 
    // 미지불금액 계산
    while (e.hasMoreElements()) {
        Order each = (Order) e.nextElement();
        outstanding += each.getAmount();
    }
 
    // 상세출력
    System.out.println ("name:" + _name);
    System.out.println ("amount" + outstanding);
}


위 코드를 보면 함수명을 보면 미지불 금액을 출력하는데,  배너도 출력 하고 상세내역도 출력 한다.


이 함수를 기능별로 분리하는 방법을 선호 하는데 이유를 생각해 본다면 다음과 같다.


메서드를 분리 한다면, 분리된 함수들에 대한 사용성이 증가한다.  아래 코드를 보면 배너출력과 상세출력을 분리하였다. 함수명만 봐도 한눈에 이해하기 쉽다. 

또한, 추후에 지불된금액에 대해 출력을 할때,  printBanner() 를 재사용 할 수 있다.





void printOwing() { Enumeration e = _orders.elements(); double outstanding = 0.0; printBanner(); // 미지불금액 계산 while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); outstanding += each.getAmount(); } printDetails(outstanding ); } // 배너출력 void printBanner() { System.out.println ("**************************"); System.out.println ("***** Customer Owes ******"); System.out.println ("**************************"); } // 상세 출력 void printDetails(double outstanding) { System.out.println ("name:" + _name); System.out.println ("amount" + outstanding); } // 추가된 지불된 금액 함수 void printPaidMoney() { printBanner(); // 함수 재사용 증가 }



미지불 금액 계산 로직도 함수로 뺄 수 있다.


void printOwing() { printBanner(); double outstanding = getOutstanding(); printDetails(outstanding ); } // 미지불금액 계산 double getOutstanding() { Enumeration e = _orders.elements(); double result = 0.0; while (e.hasMoreElements()) { Order each = (Order) e.nextElement(); result += each.getAmount(); } return result; } // 배너출력 void printBanner() { System.out.println ("**************************"); System.out.println ("***** Customer Owes ******"); System.out.println ("**************************"); } // 상세 출력 void printDetails(double outstanding) { System.out.println ("name:" + _name); System.out.println ("amount" + outstanding); }

리펙토링의 기본을 공부 했다. 

기억해야 할 부분은 함수는 한눈에 알아 보기 쉬워야 해야하며, 분리가 가능하다면 분리 하는 것이 재사용성이 좋다는 것.




저작자 표시
신고
Posted by hoonihoon85 hoonihoon
2014.06.24 16:57

1. 리펙토링이란?

- 소프트웨어를 보다 쉽게 이해할 수 있어야 하고, 동작변화 없이 내부 구조를 변경하는 것.


2. 리펙토링의 목적?

- 프로그램을 빨리 작성 할 수 있도록 도와준다.

- 코드 디자인을 개선해준다.

- Bad code -> Good code

 

3.  Bad code 란? 

 같은 작업을 위해 더 많은 코드 사용, 중복이 많고 이해하기 어렵다. 

 유지보수하기에도 어려운 코드.


4. 리펙토링은 언제하는가?

 틈틈히 계속, 기능추가할때, 버그수정할때, 코드 검토시에.


5. 리펙토링을 할 수 없을때는?

 1) 디자인 실수가 있어 마음대로 리펙토링을 할 수 없을때

 2) 현재 설계된 구조가 보안문제, 퍼포먼스 문제등 중요사항으로 리펙토링을 기대할 수 없을 때.

 3) 코드가 처음부터 작성 하는게 나을 정도로 엉망인 경우

 4) 현재 코드가 작동하지 않을 경우

 5) 마감일이 가까울 경우.


6. 리펙토링할 나쁜코드는 왜 발생하는가?

 - Copy & paste 에 의해 중복 코드 발생.

 - 잘못된 변수명, 함수에서 발생.  (일관성이 중요   add,register, put, create )

 - 특정 클래스내의 메소드가 동작을 하기 위해 다른 클래스에 있는 정보를 많이 필요로 한경우 ( 메서드를 이동한다.)

- 나쁜주석

- 너무긴 메서드, 파라미터


7. 어떤식으로 리펙토링을 시작해야 되는가?


찾기 쉬운것 부터 한다.

 측정할 수 있는 것  (주석, 긴메서드, 거대한 클래스, 긴 매개변수)

   메소드가 하는일 설명, 블록이 하는일 설명


 
 // 배열값의 각 요소들이 이에 대응되는 예측 값과 허용범위 내의 차이를 갖는지 체크한다.
  public boolean compare(int[] expected, int[] actual, int clipLimit, int delta) {
    // clipLimit보다 큰 값을 잘라낸다.
    for (int i = 0; i < actual.length; i++)
      if (actual[i] > clipLimit)
        actual[i] = clipLimit;
    // 비교하려는 두 배열값의 길이가 같은지 체크한다.
    if (actual.length != expected.length)
      return false;
    return true;
  }



다음 강으로...

저작자 표시
신고
Posted by hoonihoon85 hoonihoon
2014.04.25 14:41



출저:  http://iilii.egloos.com/3791596



자바 디자인 패턴 3Factory Method


1. Factory Method패턴은..


factory는 공장이죠. 객체를 막 찍어내는 놈입니다. 객체 선언은 보통 new 객체() 이런식으로 하죠. factory는 내부에서 그런 일을 해줍니다. 즉 factory를 가져다가 쓰는 부분에서는 new 객체()와 같은 식으로 변수를 선언할 필요가 없습니다.Abstract class나 인터페이스에 대해서 다양한 하위 구현체가 있을 경우에 사용하면 좋습니다. 사용법은 Factory.create(인자는 맘대로) 와 같이 됩니다. 


2. 예제


package chap03_StaticFactory;

public interface Animal {

    public void printDescription();

}



package chap03_StaticFactory;

public class AnimalFactory {

    public static Animal create(String animalName){

        if (animalName == null) {

            throw new IllegalArgumentException("null은 안 되지롱~");

        }

        if (animalName.equals("소")) {

            return new Cow();

        }else if (animalName.equals("고양이")) {

            return new Cat();

        }else if (animalName.equals("개")) {

            return new Dog();

        }else{

            return null;

        }

    }

}



package chap03_StaticFactory;

public class Cat implements Animal {

    public void printDescription() {

        System.out.println("쥐잡기 선수");

    }

}



package chap03_StaticFactory;

public class Cow implements Animal {

    public void printDescription() {

        System.out.println("우유 및 고기 제공");

    }

}



package chap03_StaticFactory;

public class Dog implements Animal {

    public void printDescription() {

        System.out.println("주로 집 지킴");

    }

}



package chap03_StaticFactory;

public class Test {

    public static void main(String[] args) {

        Animal a1= AnimalFactory.create("소");

        a1.printDescription();

        Animal a2= AnimalFactory.create("고양이");

        a2.printDescription();

        Animal a3= AnimalFactory.create("개");

        a3.printDescription();

    }

}


이번 것은 소스가 좀 깁니다. 일단 Animal이라는 인터페이스가 있습니다. Cat, Cow, Dog 는 이 인터페이스의 구현체들입니다.

그리고 AnimalFactory가 있는데, 여기서 Animal의 구현체를 돌려줍니다.

Test에서 new Cow()와 같이 하지 않고, AnimalFactory.create("소")를 호출하는 게 일반적인 방법과의 차이입니다.

Animal의 구현체가 더 늘어나면 어떻게 될까요? 전부 new AnotherAnimal()과 같이 생성하는 것보다는 Facotry의 create()메쏘드만 수정하는 게 좀 편하겠죠? 


3. Factory 의 유용성


Animal a1 = AnimalFactory.create("소"); 와 같은 코드에서 a1이 Cow라는 것을 굳이 신경쓰지 않겠다는 겁니다. Test클래스 안에는 new 라는 구문 자체가 없습니다. 정확히 어떤 클래스의 인스턴스인지 신경쓰지 않고 구현할 수 있는 장점이 있습니다. 객체 타입이 굉장히 유연해 질 수 있죠.


4. JAVA API에 있는 Factory Method


Factory 패턴의 중요한 특징 중 하나는 Factory에서 리턴할 때는 매번 객체를 새로 만들지 않을 수도 있다는 겁니다.

Boolean.valueOf(boolean) 을 먼저 살펴 보죠. 

        Boolean a = Boolean.valueOf(true);

        Boolean b = Boolean.valueOf(true);

        System.out.println(a==b);

이 코드를 실행시키면 어떤 결과가 나올까요? true 가 나옵니다. 왜냐하면  Boolean.valueOf(true) 는 Boolean.TRUE 라는 상수를 리턴합니다. 즉, 인스턴스를 새로 만드는 것이 아니라 기존에 있는 것을 그냥 리턴합니다. 매번 새로 만들지 않는다는 거죠. 각종 Wrapper 클래스에 있는 많은 메쏘드 들이 이렇게 구현되어 있습니다.

Calendar.getInstance() 를 호출하면, 사용자 환경에 맞는 Calendar 객체가 리턴됩니다. 보통은 GregorianCalendar가 리턴된죠.

(이 메쏘드의 이름은 좀 잘못지어진 것 같습니다. 보통 getInstance()는 singleton 패턴에서 쓰이는 이름입니다.)


5. Factory Method의 종류


예제에서는 Factory의 인스턴스를 만들지 않고, static 메쏘드인 create()만을 호출했습니다. 이런 방식을 static factory method라고 합니다.

그냥 factory method라고 하면, factory의 인스턴스를 만들어서 쓰는 방식입니다. static factory에 비해 사용 빈도는 좀 떨어지지만, factory의 인스턴스에 귀속되는 객체를 생성해야 할 때는 이런 방식을 씁니다.(static factory에 비해 많이 쓰지 않으므로 자세한 것은 생략합니다.



저작자 표시
신고
Posted by hoonihoon85 hoonihoon
2014.04.24 10:22



빌더 패턴(Builder pattern)이란 복합 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현 결과를 만들 수 있게 하는 패턴이다. _ wiki


기본 샘플


http://ko.wikipedia.org/wiki/%EB%B9%8C%EB%8D%94_%ED%8C%A8%ED%84%B4



실제 업무상 사용 하는 샘플 


http://www.javacodegeeks.com/2013/01/the-builder-pattern-in-practice.html


http://rdafbn.blogspot.ie/2012/07/step-builder-pattern_28.html


빌더패턴 수정하기


http://jlordiales.wordpress.com/2012/12/24/the-ins-and-outs-of-immutability/




Android builder pattern 샘플

https://github.com/nostra13/Android-Universal-Image-Loader/blob/master/sample/src/com/nostra13/example/universalimageloader/UILApplication.java

https://github.com/nostra13/Android-Universal-Image-Loader/blob/master/library/src/com/nostra13/universalimageloader/core/ImageLoaderConfiguration.java

저작자 표시
신고
Posted by hoonihoon85 hoonihoon
2014.01.02 23:48

Vector와 ArrayList 의 공통점

순서가 있는 Collection 이다.

데이터를 중복해서 포함할 수 있다.

List 인터페이스를 구현하고 있다.


Vector와 ArrayList 의 차이점 동기화 여부 차이 입니다.

Vector  : 자동으로 동기화를 보장 해준다.  (안전성이 좋다)

ArrayList : 멀티스레드를 이용할때 동기화를 보장해주지 않는다.  (Vector 보다 속도 빠름)


ArrayList 동기화 지원 방법

List list = Collection.synchronizedList(new ArrayList());

동기,비동기 관련 포스트 보러 가기 click click !



도움되는 글 

-질문-

채팅 서버 만들고 있는데...(동접 1000명 정도 생각중) 

vector를 쓰는게 나은가요 아니면 arraylist 동기화해서 쓰는게 나은가요?

vector가 옛날꺼지만 동기화때문에 쓰려고 하는데..(채팅서버 특성상 멀티스레드 돌리거든요)

속도도 느리고 여러모로 구형이라......

arraylist동기화 하려니 만약 동기화해서 속도가 느려지면 그밥에 그나물 이라는 생각도 들고...

아니면 vector시대에 비해 컴속도도 빨라졌고 자바도 발전했으니 

그냥 arraylist동기화 하는게 여러모로 더나은지..

그것도 아님 동기화시에 유리한 다른 벡터계열의 함수가 있는지요..

java는 1.5버전으로 하고 있습니다.

 

 

-답변-

어차피 Vector 나 ArrayList나 Serializable(직렬 선형구조(인덱스를 참조하는..))한 자료형이므로

색인 속도는 비슷하지 않을까 싶어요..

그리고 일정 수준이상 하드웨어면은 개미털만큼에 속도차이밖에 안날듯 싶네요..

더군다다 1000명정도 동접할 채팅서버면 따로 서버장비를 둘 필요없을 정도로 소규모니까

리소스때문에 뻣거나 할일은 거~~의 없을듯 싶습니다..

(네트워크가 받쳐주고 프로그램을 재대로 짰다고 가정하에...)

 

프로그램 즉 코딩하는 측면에 생각해 보면 List가 Vector 보단 쓰기 편하겠지요

Vector는 초기화에 사이즈를 주어야 하기 때문에 첨부터 메모리에 maximum 사이즈를

올려야 하고 만약 최대사이즈 이상일 경우에 동작하는 Exception 처리도 따라오겠네요..

List는 동적할당이므로 최대사이즈 없이 노드를 생성하여 붙이니까 클라이언트가

붙는만큼 메모리에 적재시킬 수 있으니까 자원관리측면에서 이득이지 않을까 싶네요

또한 노드를 릴리즈 시키면 자동으로 메모리 반환도 가능하구요

 

 

서버를 어떻게 구성할지는 모르겠지만 Request/Response 를 어떻게 구성할지도 생각해 봐야겠네요 

동기식으로 할지 비동기식으로 할지....

동기 비동기에 따라서 서버구성도 다르게 구성되야겠고 Thread를 최대 몇개까지 어떻게

쓸껀지도 속도에 큰 요인이 되겠네요..

아무래도 Thread를 많이 붙이면 클라이언트와 거의 동기식 처리가 가능하겠지만

그만큼 자원할당이나 메모리관리 등이 복잡해질 우려가 있으니 잘 고민하시기 바랍니다.

비동기식이면 단순하게 스케쥴 관리를 위한 큐와 Send-Reciver 쓰레드로도 구성할 수 있겠네요..


신고
Posted by hoonihoon85 hoonihoon
2013.12.23 19:41

시스템 프로퍼티란 시스템 환경에 관한 정보를 말한다.

System 클래스에 있는 getProperty() 메서드를 이용하면,
현재 사용하고 있는 환경 정보를 얻을 수 있다.
System 클래스를 이용하기 위해서는 java.util 패키지를 임포트 한다.


import java.util.*;
.
.
String version = System.getProperty("java.version");

 

주요 프로퍼티는 아래와 같다.
프로퍼티 : 의미 : 표시 예
java.version : Java 의 버전 : 1.4.2_13
java.vendor : Java 의 벤더(공급자) : Sun Microsystems Inc.
java.vendor.url : Java 의 벤더의 URL : http://java.sun.com
java.home : Java 를 인스톨한 디렉토리 : c:\j2sdk1.4.2_13
java.class.version : Java 클래스의 버전 : 48
java.class.path : Java 클래스가 존재하는 경로 : c:\Java
java.ext.dir : 확장기능 클래스를 포함하는 디렉토리 : null (미설정의 경우)
os.name : OS 의 이름 : Windows XP
os.arch : OS 의 아키텍처 : x86
os.version : OS 의 버전 : 5.1
file.separator : 파일을 구분하는 문자 : \(Unix 에서는 /)
path.separator : 경로를 구분하는 문자 : (Unix 에서는 :)
line.separator : 행을 구분하는 문자(개행코드) : \n
user.name : 사용자 계정 : Na
user.home : 사용자 홈 디렉토리 : c:\Document and Settings\Na
user.dir : 현재 작업 디렉토리 : c:\Java


이를 구현하기 위해 직접 코딩을 해보았다.

<a.java 의 내용>-------------------------
import java.util.*;

class a{
  public static void main(String args[]){
    System.out.println("java.version : " + System.getProperty("java.version"));
    System.out.println("java.vendor : " + System.getProperty("java.vendor"));
    System.out.println("java.vendor.url : " + System.getProperty("java.vendor.url"));
    System.out.println("java.home : " + System.getProperty("java.home"));
    System.out.println("java.class.version : " + System.getProperty("java.class.version"));
    System.out.println("java.class.path : " + System.getProperty("java.class.path"));
    System.out.println("java.ext.dir : " + System.getProperty("java.ext.dir"));
    System.out.println("os.name : " + System.getProperty("os.name"));
    System.out.println("os.arch : " + System.getProperty("os.arch"));
    System.out.println("os.version : " + System.getProperty("os.version"));
    System.out.println("file.separator : " + System.getProperty("file.separator"));
    System.out.println("path.separator : " + System.getProperty("path.separator"));
    System.out.println("line.separator : " + System.getProperty("line.separator"));
    System.out.println("user.name : " + System.getProperty("user.name"));
    System.out.println("user.home : " + System.getProperty("user.home"));
    System.out.println("user.dir : " + System.getProperty("user.dir"));
  }
}
----------------------------------------

만약, 출력결과를 텍스트 파일로 보고 싶다면,

>java a > 1.txt

라고 실행하면, 실행결과를 1.txt 파일로 출력해준다.



첨부파일: system_getProperty.zip


신고
Posted by hoonihoon85 hoonihoon
2013.12.20 16:52

Servlet 를 이용한 파일 업로드를 간단하게 완성 시켰습니다.

사진과 글이 <Form> 태그를 통해 온 것을 파싱 할 수 있는 MultipartRequest 를 사용했습니다.


1. cos.jar 를 다운 받아 주세요.

 http://www.servlets.com/cos/ 접속해서 받거나 아래 아이콘을 클릭해서 받으면 된다.

cos.jar


2. 파일업로드 서블릿을 생성하고 web.xml파일에 Servlet를 추가해주세요.


public class UploadServlet extends HttpServlet{

public UploadServlet() {

super();

}

public void init() throws ServletException {}

@Override

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {

FileManager fileManager = new FileManager(req); 

        }


3. FileManager class 를 만들어서 File을 관리할 수 있도록 했습니다.

MultipartRequest Class를 이용하면 File 복사가 되고, 파일명을 바꾸고 싶으면 마지막 파라미터를 주의 깊게 보시면 됩니다.


public class FileManager {

private MultipartRequest mRequest;

private int postMaxSize = 10 * 1024 * 1024;   //10MB

      private String encoding = "UTF-8";

public FileManager(final HttpServletRequest req) throws IOException {

mRequest = new MultipartRequest(req, 이미지저장 Path, postMaxSize, encoding, new FileAlteration());

}

}



4. 마지막 파라미터에 FileAlteration 이라는 class를 inner class 로 생성 했습니다.


class FileAlteration implements FileRenamePolicy {

@Override 

public File rename(File file) {

String parentDir = file.getParent();

String fileName = file.getName();

//Get the extension if the file has one

       String fileExt = "";

       int i = -1;

       if(( i = fileName.indexOf(".")) != -1){

           fileExt = fileName.substring(i);

           fileName = fileName.substring(0,i);

       }

       //Add the timestamp and user ID

       String userId = (String) req.getSession().getAttribute("id");

       newFileName = /*fileName + "_"+ */userId +("_"+( new Date( ).getTime( ) / 1000)) + fileExt;

      

       //piece together the filename

       newFileFullName = parentDir + 

        System.getProperty("file.separator") + newFileName;

       

       file = new File(newFileFullName);

       mimeType = new MimetypesFileTypeMap().getContentType(file);


return file;

}

}



위 소스를 보시면 쉽게 이해하실 수 있을거 같습니다.

파일작성자의 아이디와 TimeStamp 값을 이용해서 새롭게 생성될 파일의 이름을 만들어 주면 됩니다.

아래와 같이 파일이 저장됩니다.




단순한 업로드가 완성 되었습니다.


지금 하고 있는 모바일웹프로젝트에 글과+사진이 함께 전송하도록 하기 위해 한 것입니다.

파일업로드 뿐만 아니라 

게시판테이블에 글 정보 삽입, 방금 삽입된 레코드의 INDEX 를 가져오는 루틴,

파일정보를 담은 테이블에 정보 삽입, 게시판 테이블 UPDATE 까지 고려해야 합니다.





신고
Posted by hoonihoon85 hoonihoon
2013.12.19 23:17

Java 자바 4대 중첩클래스 (instance class, static class, local class, anonymous class)

 

1. 일반 중첩클래스

 

중첩클래스는 효율적인 관리를 위한 하나의 형식이다. 프로그램에서는 하나의 대표 클래스 내에 다른 클래스들을 넣어 관리할 수 있는 형식을 제공한다. 그것은 대표 클래스가 그들 모두에 있어 공통적인 속성 값만 가지고 나머지는 각기 다른 중첩 클래스에서 관리하도록 만드는 것이다. 즉, 중첩클래스란 하나의 클래스 내부에 또 다른 클래스를 사용할 수 있는 것이다. 당연히 중첩되는 클래스는 하나 이상일 수 있다.

 

그렇다면 클래스 외부에서 중첩클래스 내부의 멤버들에는 어떻게 접근할까? 우선 바깥쪾에 있는 클래슬 Outer

클래스라 하고 안쪽에 있는 클래슬 Inner클래스라 하자. 

 

■ 중첩클래스 접근 형식

Outer 객체1 = new Outer();  <=Outer클래스 객체 생성 방법

Outer.Inner 객체 2 = 객체 1.new Inner(); <=Inner클래스 객체 생성 방법

 

간단한 코딩을 통해 내부 멤버에 접근해 보자.

 

 

 

 

여기서 중첩 클래스에는 각각 Outter클래스에 x의 멤버를 Inner클래스에는 y의 멤버를 배치해 두었다. 각 데이터를 사용하는 방법은 x는 Outter클래스 멤버이기 때문에 Outer클래스의 객체를 발생시키고, y는 Inner클래스 멤버이기 때문에 Inner클래스 객체를 발생시켜 접근하면 된다.

 

또한 Inner클래스에서는 Outer클래스의 멤버를 이용할 수 있다. 아래 코딩을 보면 객체 생성없이 바로 Inner클래스에서 Outter클래스의 멤버를 이용할수 있음을 알 수 있다.

 

 

 

 

 하지만 Outer클래스에서는 Inner클래스의 멤버를 사용할 수 없다.

사용하고 싶다면 객체를 직접 발생시켜야 한다 . 다음 코딩을 보면 Outer클래스 Inner클래스를 사용하기

위해서 객체를 발생시키는걸 알 수 있다.

 

 

 

 

 

 

 

2. 정적 중첩 클래스

 

다음으로 살펴볼 것은 중첩 클래스 내부에서 static과 관련된 멤버를 선안할 수 있는 클래스이다.

이것은 단순히 클래스 이름 앞에 static이라는 예약어만 붙이면 되고, 이런 클래스를 정적(static)

중첩 클래스라고 한다.

 

class Outer{

     static class Inner{

...........

}

}

 

정적 중첩클래스는 static의 특성상 객체를 독립적으로 만들 수 있다.

 

■ 정적 중첩클래스의 객체 생성 방법

Outer.Inner  객체 = new Outer.Inner();

 

일반 중첩 클래스와 달리 Outer클래스의 객체가 없어도 Inner클래스의 객체를 만들 수 있다. 또한 static멤버를

선언할 수 있고, static메서드도 만들어 사용할 수 있다. 그래서 반드시 Inner클래스에 static을 선언하여 사용하고

싶다면 정적 중첩클래스를 사용해야 한다. 또한 객체의 선언 방식은 일반 중첩클래스의 객체 선언 방식을 사용할 수 없고 위와 같이 Outer.Inner 객체 = newr Outer.Inner(); 로만 객체를 발생시킬 수 있다. 문제는 Outer클래스의 멤버를 끌어다 쓰는 것이 문제인데, 끌어다 쓰려는 Outer클래스의 멤버가 static인 경우에만 사용할 수 있다.

 

그렇다면 만인 main()메서드를 Inner클래스에 사용하고 싶다면 어떻게 해야 할까? 먼저 main()메서드는 그 자체가 static이기 때문에 정적 중첩 클래스를 사용해야 한다.

 

 

 

 

3. 지역 중첩 클래스

 

일반적으로 클래스라는 것은 재상용을 염두해 두고 만들어진 것인데 이런 클래스가 특정 메서드를 실행할 때에만 필요하고 별로 사용할 경우가 없다면 그것을 굳이 외부에 드러나는 형태로 만들 필요는 없을 것이다. 그래서 특정 메서드에 한정적인 용도로 사용할 클래스로 지역 중첩 클래스라는 개념이 등장하였다. 그러나 이 경우는 접근 제한자와 지정 예약어를 사용할 수 없는 형태이다.

 

 

 

위의 코딩을 보면 main()메서드 안에 Inner클래스가 들어가 있는 상황이다. 여기서 만일 일반 중첩 클래스처럼 main()메서드의 멤버인 x를 사용하려면 그 멤버는 반드시 final로 선언되어 있어야 한다.

 

 

4. 익명 중첩 클래스

 

익명 중첩 클래슨느 지역 중첩 클래스의 변형된 형태라고 할 수 있다. 이것은 이름에서 알 수 있듯이 class라는 예약어와 클래스명을 가지지 않고 단지 instance의 생성과 내용부의 정의만 가진다. 한가지 주의할 점은 여기에 사용되는 중첩 클래스는 이미 기존에 존재하는 것이어야 한다. 또한 이 내부에서 사용할 수 있는 외부 멤버도 역시 final로 선언되어 있어야 한다.

 

 


출저: http://kwy1052.tistory.com/41?viewbar

신고
Posted by hoonihoon85 hoonihoon