달력

4

« 2024/4 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
2013. 6. 5. 17:50

Deque (덱) 요약 I.lib()/I.lib(Java)2013. 6. 5. 17:50

.. .. ..

[출처] [자바][기본] Deque 데크, 디큐, 덱 ( double ended queue )|작성자 카루

 

큐 + 스택 의 기능을 가진 Deque에 대해 알아보자.

 

Deque는 인터페이스므로 구현된 다른 클래스를 사용해야 한다.

 

여러가지가 있지만 본 예제에서는 LinkedBlockingDeque 를 사용한다.

 

큐+스택 인 만큼 넣는 방법도 다양하고.

 

꺼내는 방법도 다양하다.

 

그러나 큐는 먼저 넣은게 먼저나온다.

스택은 먼저 넣은게 나중에 나온다.

 

그 두개가 혼합되어 사용될경우 

즉 큐로서 넣은 값이

 

스택방식으로 꺼내질경우

어떻게 될지에 대해 상상이 가는가.

 

큐로 넣은값이 앞으로 가는가 뒤로 가는가.

스택으로 꺼내는값은 뒤에서 꺼내는것인가 앞에서 꺼내는 것인가.

 

이번 실험으로 애매함을 해결해보자.

 

물론 이 실험으로도 어디가 앞인지 뒤인지 알수 없다.

 

다만 방향을 제시할수 있다. 즉 어떤 방법은 A로 가고 어떤방법은 B에서 꺼내고 라는등...

 

결국 이것을 앞 뒤라고 이해하면 편할 것이다.

 

 

일단 꺼내는 함수들에 대해 알아보자.

 

값을 제거하지 않는 것들

peek()        : 앞

peekLast()  : 뒤

peekFirst()  : 앞

getFirst()    :  앞

getLast()    :  뒤 

값을 제거하는 것들

poll()         :  앞

pollFirst()   :  앞

pollLast()   :  뒤 

pop()         :  앞

 

입력하는 함수들에 대해 알아보자.

 

offerFirst  :  앞

offerLast  :  뒤

offer        :  뒤  

addFirst   :  앞   

addLast   :  뒤

add         :  뒤

putFirst    :  앞  

putLast    :  뒤

put          :  뒤

push       :  앞 

 

정리하자면

 

출력은 Last가 붙지 않은건 전부 앞에서 꺼낸다.

 

입력이 엄청나게 괴랄 하다.

First, Last가 붙은것들은 해당 위치로 다 들어간다.

offer, add, put은 뒤에 넣는다.

push는 앞으로 넣는다.

 

참고로 앞으로 넣는다는 얘기는 먼저넣은게 점점 뒤로 밀려나간다는 얘기이며

뒤로 넣는다는 얘기는 뒤에 먼저 들어간 순서대로 서있는다는 이야기이다.

 

테스트 소스를 첨부함니다.

잘못된 부분이 있으면 댓글로 부탁드려요.

 

[펌] : http://cafe.naver.com/mymejong/378

 

DEQUES

 

Deque는 double-ended queues의 줄임말이다(디큐가 아니라 데크로 발음함). 큐는 한쪽 엔드에서 추가하고 다른 쪽 엔드에서 제거할 수 있는 반면, 양쪽 엔드에서 추가와 제거가 가능한 double-ended queues는 스택과 큐가 결합된 것처럼 작동한다. Deque 인터페이스는 J2SE 5.0에 도입된 Queue 인터페이스에서 확장되는데, 이 기능은 최근에 Java SE 6의 Java Collections Framework에 추가되었다(이 기능이 최종적으로 포함되려면 JCP의 승인을 받아야 함). 인터페이스 구현에는 LinkedList, ArrayDeque와 이에 수반되는 LinkedBlockingDeque가 포함된다.

LinkedList는 아마도 deque의 가장 전형적인 용례일 것이다. LinkedList는 제한 없이 확장이 가능하고 양쪽 엔드에서 신속하게 추가 및 제거 작업이 가능하다. ArrayDeque는 용량 제한이 없는 또 하나의 전형적인 구현으로, 성능을 최상으로 유지하기 위한 wraparound index 구현을 제공한다. 모든 베이스 컬렉션 구현이 그렇듯이, 어느 쪽도 threadsafe하지 않다. (VectorHashtable 같은 역사적인 컬렉션은 threadsafe하지만 고도의 동시발생적인 액세스를 위해 설계되지는 않았다.) 쓰레드 안전이 필요한 경우에는 LinkedBlockingDeque를 이용하면 되는데, LinkedBlockingDeque 클래스는 Deque에서 확장되는 새로운 BlockingDeque 인터페이스를 구현한다. 이 클래스는 사이즈가 제한되거나 제한되지 않을 수도 있다. 용량이 지정되지 않은 경우, 사이즈 제한은 Integer.MAX_VALUE이다.

다음 메소드 중 하나로 deque에 엘리먼트를 추가할 수 있다.

  • void addFirst(E e)
  • void addLast(E e)
  • boolean add(E e)

add() 메소드는 addLast()와 동등하다고 볼 수 있다. deque의 용량이 부족하면 IllegalStateException이 throw된다. 또한 다음 메소드 중 하나를 통해 추가될 엘리먼트를 제공할 수도 있다.

  • boolean offer(E e)
  • boolean offerFirst(E e),
  • boolean offerLast(E e)

addXXX() 메소드로 엘리먼트를 추가하는 경우와 달리, offerXXX() 메소드가 제공되었을 때 항목을 추가할 수 없으면 메소드는 false를 반환한다.

이 외에도 엘리먼트 제거를 위한 한 쌍의 메소드 세트가 있다.

  • remove(), removeFirst(), and removeLast()
  • poll(), pollFirst(), and pollLast()

deque가 비어있을 경우 removeXXX() 메소드는 NoSuchElementException를 throw하고, pollXXX() 메소드는 null을 반환한다. 또한, 아래의 메소드 중 한 가지를 사용하여 특정 개체를 제거할 수 있다(deque가 엔드에서의 추가/제거의 용도로만 의도된 경우라도).

  • boolean remove(Object o)
  • boolean removeFirstOccurrence(Object o)
  • boolean removeLastOccurrence(Object o),

Deque는 엘리먼트를 검사하기 위한 6개의 메소드를 가진다.

  • element()
  • getFirst()
  • getLast()
  • peek()
  • peekFirst()
  • peekLast()

element()는 오래된 Queue 인터페이스로부터 상속한 인터페이스 메소드이기 때문에 get() 메소드가 없다. get 메소드는 removeXXX()와 유사하며 deque가 비어있는 경우 NoSuchElementException을 throw하는데, 이와 대조적으로 peek 메소드는 비어있는 경우 null을 반환한다. 이는 물론 Deque가 null 값의 추가를 허용할 경우 deque의 엔드에 있는 null 항목과 ‘nobody on deck’ 간의 차이를 알 수 없다는 것을 의미한다. 그러나 이 경우에는 size() 메소드를 활용할 수 있다.

Deque는 개념상 이중으로 링크되므로, 어떤 순서로도 엘리먼트들을 traverse할 수 있다. 앞에서 뒤로 traverse하려면 iterator()를 이용하고 역순, 즉 뒤에서 앞으로 traverse하려면 descendingIterator()를 이용한다. 하지만 위치별로 엘리먼트에 액세스할 수는 없다?적어도 Deque 인터페이스로는 불가능하다. LinkedListDeque의 구현이지만 함께 구현되는 List 인터페이스를 통해 색인 액세스(indexed access)를 지원한다. 랜덤 액세스 요건이 없다면 Deque 구현이 더 효과적으로 이루어질 수 있다.

왜 deque를 사용하는 것일까? deque는 maze 또는 parsing 소스를 통한 검색과 같은 반복적인 문제에 특히 유용한 데이터 구조로서, path를 따라 이동하면서(path가 양호하다고 판단되는 한) "good" spot을 저장하고 계속해서 데이터를 추가할 수 있다. path가 bad를 반환할 경우에는 bad 비트를 pop off하여 마지막 good spot으로 복귀한다. 이 경우 스택과 같은 동일한 엔드에서 추가와 제거를 수행하게 된다. 일단 길을 찾으면 처음부터 다시 시작하여 반대쪽 엔드에 해당하는 솔루션을 밝혀낸다. 또 다른 전형적인 예로, 운영체제 스케줄러, 그리고 사람들을 속이기를 좋아하는 악질 카드 딜러 등을 들 수 있을 것이다.

다음 프로그램 BlockedDeque의 용례, 보다 구체적으로는 용량 제한이 있는 LinkedBlockingDeque를 보여주고 있다. 이는 물론 최상의 deque 용례는 아니지만 API와 용량 제한에 도달했을 때의 상황을 예시해준다. 프로그램은 23개의 달 이름(짧은 이름과 긴 이름 모두)을 취하여 이를 6-엘리먼트 블로킹 deque의 헤드에 한번에 하나씩 추가한다. 또 다른 스레드에서는, 현재 컬렉션 내에 있는 엘리먼트의 수를 토대로 엘리먼트를 deque의 헤드와 테일에서 제거한다.

   import java.io.*;
   import java.util.*;
   import java.util.concurrent.*;

   public class Blocked {
     public static void main(String args[]) {
       Calendar now = Calendar.getInstance();
       Locale locale = Locale.getDefault();
       final Console console = System.console();
       final Map<String, Integer> names = now.getDisplayNames(
           Calendar.MONTH, Calendar.ALL_STYLES, locale);
       console.printf("Starting names: %s%n", names);
       final Deque<String> deque = 
           new LinkedBlockingDeque<String>(6);
       // Add one at time to beginning of deque
       new Thread() {
         public void run() {
           Set<String> keys = names.keySet();
           Iterator<String> itor = keys.iterator();
           String element = null;
           while (itor.hasNext() || element != null) {
             if (element == null) {
               element = itor.next();
               console.printf("MapGot: %s%n",  element);
             }
             console.printf("Offering: %s%n", element);
             if (deque.offerFirst(element)) {
               console.printf("MapRemoving: %s%n", element);
               itor.remove();
               element = null;
             } else {
               try {
                 Thread.sleep(250);
               } catch (InterruptedException ignored) {
               }
             }
           }
           // Done. Give time to process rest.
           try {
             Thread.sleep(3500);
           } catch (InterruptedException ignored) {
           }
           System.exit(0);
         }
       }.start();
       while (true) {
         if ((deque.size() % 2 == 1)) {
           // remove head
           console.printf(
               "Remove head: %s%n", deque.pollFirst());
         } else {
           // remove tail
           console.printf(
               "Remove tail: %s%n", deque.pollLast());
         }
         // Sleep between loops
         try {
           Thread.sleep(500);
         } catch (InterruptedException ignored) {
         }
       }
     }
   }

아래에서 보듯이, 프로그램 실행 시 printf 선언문 때문에 많은 아웃풋이 생성된다. 엘리먼트를 소스 맵에서 가져오거나, 소스 맵에서 제거하거나, deque에 제공하거나, deque에서 제거할 때마다 아웃풋 행이 생성된다. deque가 full 상태일 때 어떻게 제공(offering) 동작이 복수로 발생하는지 유의할 것.

   >> java Blocked


   Starting names: {Jun=5, March=2, December=11, April=3, 
   November=10, September=8, October=9, Sep=8, Aug=7, Apr=3, 
   May=4, June=5, Feb=1, Dec=11, Oct=9, Jan=0, Mar=2, Jul=6, 
   August=7, January=0, February=1, July=6, Nov=10}
   Remove tail: null
   MapGot: Jun
   Offering: Jun
   MapRemoving: Jun
   MapGot: March
   Offering: March
   MapRemoving: March
   ...

   Remove tail: Jul
   Remove head: Nov
   Remove tail: August
   Remove head: July
   Remove tail: January
   Remove head: February
   Remove tail: null

여기서 두 가지 사실에 주목할 필요가 있다. 첫째, 24개가 아닌 23개의 짧고 긴 달 이름이 있다(5월인 "May"의 경우에는 짧은 이름과 긴 이름 모두에 해당하기 때문에 23개가 된다). getDisplayNames() 메소드는 Map을 반환하므로 "May"는 짧은 이름과 긴 이름 등 2개 엔트리에 대한 key가 될 수 없다. 둘째는, 한쪽 엔드에서 추가하고 다른 쪽 엔드에서 제거하는 것이 작업의 전부라면 Deque 대신 컬렉션 프레임워크에서 Queue 구현을 이용하는 편이 더 나을 것이라는 점이다.

최소한, STL(Standard Template Library)에서의 유용한 deque/vector 비교 자료를 보려면 An In-Depth Study of the STLDeque Container를 참조할 것(여기서는 두 컨테이너 타입의 성능 차이에 대해 다루고 있음). 자바 플랫폼 상의 실제 수에는 차이가 있지만 전반적인 개념은 나름대로 정확하다고 볼 수 있다.

 

 

.
:
Posted by .07274.
2013. 4. 23. 11:45

[펌] ByteBuffer 설명 정리 I.lib()/I.lib(Java)2013. 4. 23. 11:45

.. .. ..

출처 : http://my.dreamwiz.com/conbox/deepjava/nio/nio_home.htm

~~~ 깔끔한 정리

<< 복받으실 거에요~~ ^^>>

New I/O(java.nio)

1. nio 패키지 소개

New I/O는 JDK1.4에서 새로 추가된 패키지이다. JDK1.4의 정식 명칭은 Java 2 Standard Edition JDK1.4이다. 흔히 Meriln이라고 부르는데 이는 개발시 프로젝트의 이름이다. 참고로 Meriln은 중세시대 아더왕의 전설에 나오는 마법사의 이름이기도 하지만 쇠황조롱이라는 매의 일종인 새의 이름이기도 하다.

New I/O는 java.nio 패키지로 제공되는 기능으로 크게 버퍼 관리 클래스류, 확장된 네트워크 그리고 파일 I/O, 문자 집합 지원, 그리고 정규식 문자 표현에 새로운 특징들과 개선된 성능을 제공한다. java.nio 패키지는 다음과 같은 클래스류로 나누어진다.

  • java.nio
  • java.nio.channels
  • java.nio.channels.spi
  • java.nio.charset
  • java.nio.charset.spi

여기서 spi가 붙은 것을 볼 수 있는데 이는 SPI(Service Provider Interface)로 프로그래머가 제공하는 클래스로 대체할 수 있는 기능을 제공해준다. 이는 관련된 클래스들의 기본 구현을 프로그래머가 바꿀 수 있다는 뜻이 된다. 단, 이것은 특별한 경우에만 해당되므로 이런 것이 있다는 정도만 알아두자.



1> 특징.

  • 기본 데이터형용 버퍼를 클래스로 제공해 준다.
  • Character-set 인코더들과 디코더.
  • 패턴과 어울린 기능은 Perl-style 정규식들로 설정.
  • 채널, 새로운 I/O 추상화.
  • 메모리 매핑과 파일의 lock(잡금장치)를 지원해주는 인터페이스.
  • non-blocking 입출력이 가능.

2> 패키지 소개

  • java.nio.package : 자바 기본형 유형에 맞는 버퍼 클래스들.
  • java.nio.channels.package : 채널과 셀렉터.
  • java.nio.charset.package : 문자 암호화들.
  • java.nio.channels.spi.package : Service 프로바이더는 채널들을 위해 분류.
  • java.nio.charset.spi.package : Service 프로바이더는 문자셑를 위해 분류.
  • java.util.regex.package : 정규식들에 의해서 지정된 패턴들에 대해서 문자 표현에 대한 클래스들.
  • java.lang.CharSequence.인터페이스 : 다양한 종류의 문자 순서에의 통일된 read 전용 액세스를 제공.

3> 정리

java.nio 패키지에서 눈여겨 봐둘것은 지금까지 지원이 되지 않았던 io의 nonblocking의 지원이다. 이러한 nonblocking의 지원으로 주요한 장점은 크게 다음 두가지이다.

  1. 스레드는 더이상 읽기나 쓰기에 블록킹되지 않는다.
  2. selector의 도입으로 클라이언트의 많은 접속을 처리할 서버의 부하가 상당히 줄어든다.

그리고 또 다른 한가지 java.nio 패키지에서 네트워크 기능이 강화 되었다. 채널의 도입으로 새롭게 강화된 네트워크를 다루려면 반드시 java.net 패키지의 이해는 필수이다.

==> 여기서는 nio패키지를 크게 두부분으로 나누어서 강좌를 할 예정이다. 하나는 java.nio의 Buffer류 클래스와 java.nio의 Channels 클래스이다.

2. nio의 Buffer

1> Buffer 계층도

nio패키지에는 각종 데이터를 담는 Buffer류 클래스들이 있다. 이들 클래스는 모두 Buffer클래스를 상속받는데 버퍼 클래스는 여러 종류가 있어서 각각 기본형 데이터에 맞는 클래스들이 존재한다. 이들은 다음과 같은 클래스 계층도를 가진다.

가장 기본이 되는 클래스는 Buffer 클래스이며 여기에서 기본적인 메서드들이 정의된다. 이를 상속받은 기본형 데이터를 위한 Buffer클래스들이 있다. 단, boolean형에 대한 버퍼는 없다. 전반적인 클래스를 정리해 보자.

2> 클래스의 개요

클래스명
정의
Buffer 가장 상위 클래스로서 기본 메서드를 가지고 있다.
ByteBuffer byte 버퍼
ByteOrder 저장되는 바이트 순서의 열거 형태에 따른 버퍼
CharBuffer char 버퍼
FloatBuffer float 버퍼
IntBuffer int 버퍼
LongBuffer long 버퍼
MappedByteBuffer 파일의 메모리 맵 영역을 내용으로 하는 direct byte 버퍼(데이터가 저장되는 메모리에 따른 버퍼)
ShortBuffer short 버퍼
DoubleBuffer double 버퍼

표을 보면 낯설은 클래스가 두개 보인다. 바로 MappedByteBuffer 클래스와 ByteOrder 클래스이다.

먼저 MappedByteBuffer 클래스는 메모리 관리방식이 다른 클래스와 다른데, 버퍼는 만들기에 따라서 자바 가상머신내에 객체가 저장되는 메모리 공간인 Heap 버퍼가 있고 그외 다른 것들(변수들)이 저장되는 일반 버퍼가 있다. 여기서 Heap영역이 아닌 일반 메모리에 직접 접근하는 버퍼를 Direct Buffer라고 표현하는데 이 곳을 메모리 공간을 잡는 클래스이다.

ByteOder 클래스는 데이터가 어떤 순으로 구성될 것인지를 나타낸다. 컴퓨터는 두 바이트이상을 처리할때 처리효율이나 CPU 디자인 상의 문제로 데이터의 앞 바이트부터냐 뒤 바이트부터냐에 대한 해석의 순서에 차이가 있다. 이러한 차이는 데이터를 외부로 보내거나 받을때 영향을 미치기 때문에 데이터를 다루는 버퍼도 이를 고려해야 한다. 앞 바이트를 먼저 처리하는 CPU를 Big endian 이라고 표현하고 뒤 바이트를 먼저 처리하는 CPU는 앞 바이트에서 끝난다고 해서 Little endian 이라 한다. 만약 Little endian으로 동작하는 컴퓨터에서 만든 데이터 파일을 Big endian로 동작하는 컴퓨터에서 읽어 들여야 한다면 ByteOder 클래스로 데이터를 맞추어서 제대로 된 데이터를 얻을 수 있다. ByteOder 클래스의 클래스 메서드인 nativeOrder()메서드는 현재 동작하고 있는 컴퓨터의 CPU가 Big endian인지 Little endian인지 알려준다. 그럼 테스트를 해보자. 당장 메모장을 열고 다음과 같이 코딩을 해보자.........


/* ByteOrder 클래스의 nativeOrder() :
컴퓨터가 데이터를 어떤 순으로 처리하는지 검사
*/

import java.nio.*;

class BigLittleTest {
public static void main(String[] args) {

System.out.println("운영체제 종류 : "+System.getProperty("os.name")+"\n"+
"기본이 되는 네이티브의 바이트 순서 :"+ByteOrder.nativeOrder());

}
}

3> 예외 클래스의 개요

버퍼 클래스에서도 예외가 있는데 주로 버퍼가 다 찼을때, 데이터를 집어넣으러고 하는 경우, 버퍼에서 읽어올 데이터가 없을경우 발생하는 예외다. 역시 이들 예외도 클래스로 미리 정의 되어 있다. 다음 표는 버퍼에 관련되 예외 클래스이다.

클래스명
정의
BufferOverflowException
데이터를 put()할때 버퍼의 limit에 이르렀을 때 슬로우 되는, 체크되지 않는 예외.
BufferUnderflowException 데이터를 get()할때 버퍼의 limit에 이르렀을 때 슬로우 되는, 체크되지 않는 예외.
InvalidMarkException 마크가 정의되어 있지 않은 상태로 버퍼를 리셋트 하려고 했을 때에 슬로우 되는, 미검사 예외.
ReadOnlyBufferException read 전용의 버퍼상에서 put 나 compact 라고 하는 컨텐츠 변경 메소드가 호출되면, 발생하는 예외.

BufferOverflowException나 BufferUnderflowException 경우 버퍼 클래스류를 사용하면서 가장 많이 발생하게 될 예외가 될 것이다.....

자, 이제 버퍼 클래스를 자세히 살펴보자. 각 클래스의 메서드들은 파라미터 값만 각 데이터에 맞게 다르고 중복되는 메서드가 많으므로 주요 메서드를 위주로 살펴본다.

자바가상머신은 Big endian이다....

자바가상머신도 일종의 독립된 컴퓨터이기 때문에 이런 문제를 취급하는데 자바는 어떤 환경이든 실행되어야 한다는 기본 전제가 있다. 그래서 자바 클래스 파일은 실행되는 컴퓨터의 CPU에 관계없이 무조건 Big endian으로 동작하게끔 되어있다. 사실 이런 과정은 자바가상머신이 외부 CPU와 데이터 교환을 할때 자동적으로 처리되므로 사용자가 신경쓸 필요가 없다.

3. 버퍼 기본 동작

Buffer 클래스류들을 살펴 보기전에 버퍼의 일반적인 동작에 대해 알아보자.

1> 버퍼의 기본 구조

버퍼는 시작과 끝이 잇는 일직서의 모양의 데이터 구조를 가진다. 버퍼는 객체 생성시 크기가 결정이 되며 한번 결정된 크기는 절대로 변하지 않는다. 따라서 크기를 늘이고자 한다면 다시 객체를 생성해 주어야 한다. 기본형 데이터들은 데이터 유형에 맞게 버퍼 객체를 생성해서 조작을 하며 이들 클래스들의 공통된 동작을 위해 java.nio.Buffer 클래스가 존재한다.

Buffer는 데이터를 관리하게 위해서 position,limit,capacity라는 중요한 값을 가지고 있다.

  • position : 현재 읽거나 쓸 버퍼에서의 위치값이다. 인덱스 값이기 때문에 0부터 시작하며, 음수를 가지거나 limit보다 큰값을 가질 수 없다. 만약 position과 limit 의 값이 같아진다면 더이상 데이터를 쓰거나 읽을 수 없다는 뜻이 된다.
  • limit : 버퍼에서 읽거나 쓸 수 있는 위치의 한계를 나타낸다. 이 값은 전체 버퍼에서 얼마큼의 메모리 공간을 사용할 것인지를 지정하는 것으로 capacity 보다 작거나 같은 값을 가지며 음수 값은 가지지 않는다. 인덱스 값이기 때문에 0부터 시작하며 최초 버퍼를 만들었을때는 capacity와 같은 값을 가진다.
  • capacity : 버퍼가 사용할 수 있는 최대 데이터 개수(메모리 크기)를 나타낸다. 크기는 음수가 되지 않으며 한번 만들어지면 절대 변하지 않는다. 인덱스 값이 아니라 수량임을 주의하자,.

    그외...
  • mark : reset 메소드를 실행했을 때에 돌아오는 위치를 지정하는 인덱스로서 mark()로 지정할 ?있다. 주의할 것은 지정시 반드시 position 이하의 값으로 지정해 주어야 한다. position나 limit의 값이 mark 값보다 작은 경우,mark는 실행되지 않는다. mark()가 정의되어 있지 않은 상태로 reset 메소드를 호출하면 ,InvalidMarkException 가 발생한다.

각 값들의 관계는 다음과 같다.

0 <= mark <= position <= limit <= capacity

예를 들어 전체 크기가 10인 버퍼를 생성했다면 이를 다음과 같이 그림으로 나타내 본다면...,

0
1
2
3
4
5
6
7
8
9
(pos)
(limit) (capacity)

각 숫자는 버퍼의 인덱스를 나타내고 처음 버퍼가 생성되면 그 초기값은 position이 0, limit는 capacity 값과 같으며 capacity 는 버퍼의 전체 크기를 가진다.

버퍼에서는 데이터를 효과적으로 다루기 위해서 capacity 안에서 position과 limit을 적절히 조절하게 된다.따라서 이들 세 값들을 잘 다룬다면 버퍼는 사용하기는 아주 편리할 것이다.

2> Buffer류 클래스의 get/put(읽기/쓰기) 메서드

버퍼의 동작에서 가장 중요한 동작은 바로 데이터를 읽고 쓰기일 것이다. 이때 사용하는 것이 put()과 get()이다. 이 메서드는 모든 버퍼류 클래스에서 사용하는데 크게 상대적 get/put과 절대적 get/put이 있다. 우선 상대적 get/put을 설명한다.

1) 상대적 get/put(읽기/쓰기)

상대적이라고 하는 이유는 position의 위치에 상대적인 동작을 하기 때문인데 position이 limit를 향해 읽거나 쓴만큼 증가한다. 따라서 상대적인 get/put 동작을 버퍼에 하면 현재 position에 있는 내용을 읽거나 positon 위치에 데이터를 쓴 다음 position값이 증가한다. 이 값은 limit과 같을 때까지 증가한다. 일단 position값이 limit값과 같아지면 그 다음부터는 get/put 동작을 할 수 없고 이들 값을 다시 재설정하는 메서드를 사용해야 한다. 만약 position값이 limit값까지 증가했는데도 get() 메서드를 사용한다면 BufferUnderflowException이 발생하며 put() 메서드를 사용한다면 BufferOverflowException이 발생한다.

2) 절대적 get/put(읽기/쓰기)

절대적이라 함은 get/put 동작시 현재의 position에 영향을 받지 않는 상태에서 limit 한도내에서 절대위치의 값을 읽거나 쓸 수 있다는 뜻이다. get/put 메서드 중에 인덱스 값을 인자로 받는 메서드가 있는데 이들 메서드를 사용해서 절대위치의 값을 읽거나 쓸 수 있다.

한꺼번에 여러 개의 데이터를 get/put 를 할 수 있는데 메서드 인자로 해당 데이터 배열을 넣어주면 된다. 예를 들어 CharBuffer클래스인 경우, get(char[] c)로, ByteBuffer클래스인 경우 put(byte[] b)로 사용한다. 이것은 position에 대해 상대적인 동작을 하며 역시 인자로 인덱스값을 주는 경우 절대위치에서 조작을 할 수 있다.

==> get/put 동작은 get()과 put()메서드로 수행되는데 이 메서드는 Buffer 클래스의 하위 클래스류에서 모두 가지고 있다. 동작은 같으나 인자나 리턴형은 다루는 데이터형-클래스-에 따라 다르다.

3> 테스트하기

이제 버퍼를 사용하는 간단한 예제를 다루어 보자. 우선 버퍼를 사용하려면 버퍼를 생성해야 한다. 당연하다. 버퍼를 생성하는 것은 간단하다. 각 XXXBuffer 클래스이 가지고 있는 클래스 메서드인 allocate() 메서드를 사용하면 간단하게 버퍼를 생성할 수 있다. 이때 인자로 버퍼의 초기 크기를 지정해준다. 형식은 다음과 같다.

IntBuffer buf=IntBuffer.allocate(10);

이는 int형 데이터를 다루는 크기가 10인 버퍼를 생성한다. 이제 예제를 보자.

import java.nio.*;

public class BufferTest1 {
public static void main(String[] args) {
//크기가 10인 IntBuffer버퍼 객체 생성
//이때 초기값은 pos는 0, limit는 capacity 값과 같다.
IntBuffer buf=IntBuffer.allocate(10);
System.out.println(buf);

buf.put(11);
System.out.println(buf.get(0));
System.out.println(buf);

buf.put(12);
System.out.println(buf.get(1));
System.out.println(buf);

buf.put(13);
System.out.println(buf.get(2));
System.out.println(buf);

buf.get(7);
buf.put(14);
buf.get(9);

System.out.println(buf);
}
}

<< 실행 결과 >>


C\>java BufferTest1

java.nio.HeapIntBuffer[pos=0 lim=10 cap=10]
11
java.nio.HeapIntBuffer[pos=1 lim=10 cap=10]
12
java.nio.HeapIntBuffer[pos=2 lim=10 cap=10]
13
java.nio.HeapIntBuffer[pos=3 lim=10 cap=10]
java.nio.HeapIntBuffer[pos=4 lim=10 cap=10]

우선 버퍼 객체를 생성하고 이를 그대로 화면에 출력을 하면 position, limit, capacity가 차례대로 출력됨을 알 수 있는데 이는 toString()메서드 때문이다. 맨처음 출력은 버퍼의 초기값을 출력해 준다. pos는 0이고 lim과 cap는 값이 같다. 그리고 put()메서드를 이용해서 버퍼의 0번자리에 '11'이라는 값을 넣고 get()을 이용해서 출력한다. 그리고 나서 버퍼 객체를 출력하면 pos가 1칸 이동한 것을 알 수 있다. 그리고 총 5번의 put()메서드를 이용해서 값을 버퍼에 넣으니 pos가 put한 만큼 이동한 것을 알 수 있다. 결과를 보면 get을 하고 나면 pos는 이동하지 않는 다는 것을 알 수 있다. 이를 그림으로 나타내면... 다음과 같다.

11
12
13
14
0
0
0
0
0
0

0
1
2
3
4
5
6
7
8
9

(pos=4)
(limit,capacity=10)


Heap Buffer와 Direct Buffer

위의 예제 소스 결과에서 IntBuffer 객체를 출력하니 java.nio.HeapIntBuffer가 출력됨을 알 수 있다. 이는 버퍼 객체를 생성하면 메모리에 적절한 내부 구현 객체가 생기는데 allocate() 메서드로 생성하면 Heap Buffer를 얻을 수 있다. 이는 앞서 MappedByteBuffer 클래스 소개시 잠깐 언급을 했는데 Heap Buffer는 자바 가상머신내에 객체가 저장되는 메모리 공간인 Heap 버퍼를 말한다. 따라서 allocate() 메서드로 생성된 버퍼에서는 데이터가 Heap 버퍼에 저장이 된다. 하지만 이 공간이 아닌 일반 메모리 공간에 버퍼를 생성할 수 있는데 이를 Direct Buffer라 한다. 이 버퍼는 allocateDirect()메서드로 생성할 수 있다. Direct Buffer로 만들면 운영체제가 제공하는 메모리 입출력기능을 직접 쓰기때문에 입출력 속도가 매우 빠른 장점을 가지고 있다. 하지만 만들때에는 시간이 걸린다. 따라서 Direct Buffer는 대용량의 데이터를 다루고 오랜 기간동안 쓰면서 빠른속도가 필요한 곳에 적합하다. 단, Direct Buffer는 ByteBuffer 클래스만 사용가능하다.

4. Buffer 클래스(java.nio.Buffer)

nio 패키지에서는 boolean형을 제외한 자바 기본 데이터형을 다룰 수 있는 Buffer류 클래스를 제공해 준다. Buffer 클래스는 버퍼로서의 기본적인 기능을 정의하며 이를 상속받는 기본형 데이터를 위한 Buffer 클래스들이 있다. 메모리 매핑 버퍼인 MappedByteBuffer 클래스와 CPU 타입에 따른 Big endian과 Little endian 구분을 위한 ByteOrder 클래스도 있다. 이들 클래스는 다루는 데이터형에 따라 XXXBuffer 라는 이름으로 클래스가 정의 되어있는데 그중에서 가장 상위 클래스는 Buffer 클래스이다. 따라서 버퍼 클래스류들은 이 Buffer 클래스가 가지고 있는 메서드는 다 사용할 수 있다.

이제 기본 메서드를 살펴보자.

1> Buffer 클래스의 메서드

ㅁ public final int capacity() : 이 버퍼의 용량(전체크기)를 리턴한다.

ㅁ public final int position() : 이 버퍼의 위치를 리턴.
ㅁ public final Buffer position(int newPosition) : 이 버퍼의 위치를 newPosition으로 설정한다.

ㅁ public final int limit() ; 이 버퍼의 limit를 리턴.
ㅁ public final Buffer limit(int newLimit) : 이 버퍼의 limit를 newlimit으로 설정한다.

ㅁ public final Buffer mark() : 이 버퍼의 현재 위치에 마크를 설정.

ㅁ public final Buffer reset()
: 버퍼의 위치를 이전에 마크 한 위치에 되돌린다. 그렇다고 마크의 값이 마크가 파기되거나, 변경되지 않는다.
==> 예외: InvalidMarkException - 마크가 설정되어 있지 않은 경우에 reset()메서드를 호출한 경우 발생

ㅁ public final Buffer clear()
: 이 버퍼의 위치(position)는 0으로 limit와 capacity값과 같게 설정한다. 마크값은 파기된다. 한 번 사용한 버퍼를 새롭게 다시 쓰기 위해 설정한다. 이 메소드는, read 조작 (put)을 실행하기 전에 주로 호출한다. 단, 버퍼내의 데이터를 지우는 것은 아니다. 단지 position과 limit를 초기값으로 설정한다.

ㅁ public final Buffer flip()
: 이 버퍼의 위치(position)는 0으로 limit와 position값과 같게 설정한다. 마크값은 파기된다. 이 메서드는 read 조작 (put)뒤, write()나 get()하기전에 이 메소드를 호출한다.

==> 예 buf.put(magic); // Prepend header
in.read(buf); // Read data into rest of buffer
buf.flip(); // Flip buffer
out.write(buf); // Write header + data to channel

ㅁ public final Buffer rewind()
: 이 버퍼의 위치만 0 으로 설정되어 마크는 파기된다. 이는 이미 put한 것을 다시 get할때나 이미 put한 것을 취소할때 사용한다.

ㅁ public final int remaining()
: 현재 위치에서 limit까지 읽어들일 수 있는 데이터의 개수를 리턴한다.

ㅁ public final boolean hasRemaining()
: 현재 위치에서 limit까지 하나 이상의 차이가 나는지를 boolean형으로 리턴해 준다. 하나 이상의 차이가 나면 하나 이상의 데이터를 get()하거나 put()할 수 있기 때문에 BufferOverflowException 이나 BufferUnderflowException 을 피하기 위한 목적으로 사용된다. 이 버퍼내에 요소가 1 개 이상 존재하는 경우 true를 리턴.

ㅁ public abstract boolean isReadOnly()
: 현재 이 버퍼가 get()만 가능하며 put()은 금지되어있는지 알려준다.

2> 예제


import java.nio.*;

class BufferMethod {
public static void main(String[] args) {
//크기가 10인 IntBuffer 클래스 객체를 생성.
IntBuffer buf=IntBuffer.allocate(10);
System.out.println("생성된 후:"+buf);

for(int i=1;i<=5;i++){
buf.put(i);//1에서 5까지 버퍼에 저장
}
System.out.println("1에서 5까지 저장후 : "+buf);

buf.flip();//pos:0, limit=pos
System.out.println("flip():"+buf);

System.out.println("remaining():"+buf.remaining());//읽어들일수 있는 데이터수

buf.clear();//pos:0 , limit=cap
System.out.println("clear():"+buf);

System.out.println("remaining():"+buf.remaining());

for(int i=0;i<10;i++){
System.out.print(buf.get(i)+",");//출력

}

buf.position(5);//현재 버퍼의 위치를 5로 지정

System.out.println("\n"+"버퍼의 pos를 5로 설정한 후 :"+buf);

for(int i=101;i<=105;i++){
buf.put(i);//1에서 5까지 다시 다른 값을 버퍼에 저장
}

buf.rewind();//pos 값만 0으로

while(buf.hasRemaining()){
System.out.print(buf.get()+",");//출력
}
System.out.println("\n"+"isReadOnly() : "+buf.isReadOnly());

}
}

<< 실행 결과 >>


C\>java BufferMethod

생성된 후 : java.nio.HeapIntBuffer[pos=0 lim=10 cap=10]
1에서 5까지 저장후 : java.nio.HeapIntBuffer[pos=5 lim=10 cap=10]
flip() : java.nio.HeapIntBuffer[pos=0 lim=5 cap=10]
remaining() : 5
clear() : java.nio.HeapIntBuffer[pos=0 lim=10 cap=10]
remaining():10
1,2,3,4,5,0,0,0,0,0,
버퍼의 pos를 5로 설정한 후 : java.nio.HeapIntBuffer[pos=5 lim=10 cap=10]
1,2,3,4,5,101,102,103,104,105,
isReadOnly() : false

이 예제는 편의를 위해 IntBuffer를 사용했다. 소스를 보면 먼저 크기 10의 버퍼를 만들고 화면에 출력을 했는데 position이 0이고 limit와 capacity가 10임을 확인할 수 있다.(pos=0 lim=10 cap=10) 이것이 버퍼의 초기값이다. 그러고 나서 put()을 한 이후에 position이 put()한만큼 이동한 것을 알 수있다.(pos=5 lim=10 cap=10) 그리고 flip() 메서드를 호출하고 확인하면 position가 0으로 가고 limit가 position위치로 오는 것을 확인한다.(pos=0 lim=5 cap=10) remaining()메서드로 position에서 limit까지 5개의 데이터가 있음을 알수 있다. clear()를 호출해서 다시 limit를 capacity와 같은 값으로 설정하고(pos=0 lim=10 cap=10) remaining()메서드를 호출하면 이번에는 position에서 limit까지 10개의 데이터가 있음을 알수 있다. get을 해서 화면에 데이터를 출력하면 값이 10개 출력됨을 알 수 있다.(1,2,3,4,5,0,0,0,0,0) position(5)으로 설정을 하고(pos=5 lim=10 cap=10) 101에서 105까지 값을 put한다. position를 5로 설정한 이유는 기존의 값을 유지하고 다음 데이터를 이어쓰기위해서이다. 다시 101에서 105까지 put하고 나면 position는 10이되는데 이를 get하기 위해 rewind()메서드를 호출해서 다시 position을 0으로 설정한다.(pos=0 lim=10 cap=10) 이렇게 채워진 값들을 while 반복문에서 hasRemaining()를 조건으로 검사해서 get한다. 좀 복잡한거 같지만 천천히 그림을 그려 보면 훨씬 이해가 쉽다. 다음 그림을 보고 정리하자...

1. 버퍼 생성 --> 아직 데이터가 put되지 않았다. 그럼 기본 초기값이 들어간다.

0
0
0
0
0
0
0
0
0
0
0
1
2
3
4
5
6
7
8
9
(pos=0)
(limit=capacity=10)

2. 1에서 5까지 put한다.

1
2
3
4
5
0
0
0
0
0
0
1
2
3
4
5
6
7
8
9
(pos=5)
(limit=capacity=10)

3. flip() 메서드 호출

1
2
3
4
5
0
0
0
0
0
0
1
2
3
4
5
6
7
8
9
(pos=0)
(limit=5)
(capacity=10)

4. clear() 메서드 호출

1
2
3
4
5
0
0
0
0
0
0
1
2
3
4
5
6
7
8
9
(pos=0)
(limit=capacity=10)

5. position(5) 메서드 호출

1
2
3
4
5
0
0
0
0
0
0
1
2
3
4
5
6
7
8
9
(pos=5)
(limit=capacity=10)

6. 101에서 105까지 값을 put()

1
2
3
4
5
101
102
103
104
105
0
1
2
3
4
5
6
7
8
9
(pos=limit=capacity=10)

7. rewind()메서드 호출

1
2
3
4
5
101
102
103
104
105
0
1
2
3
4
5
6
7
8
9
(pos=0)
(limit=capacity=10)

8. 마지막으로 while 반복문에서 hasRemaining()를 조건으로 검사해서 get해서 데이터를 출력...끝......

메서드의 연쇄호출 가능이 가능하다.

위 메서드 대부분이 position의 이동을 설정하는 것이다. 그럼 내가 원하는 위치를 얻기위해서 여러 메서드르 호출해야 하는 경우가 많다. 이런 경우 하나하나 호출하는 것보다 연결해서 사용할 수도 있다. 예를 들어 다음과 같은 문장이 있다고 하자.

b.flip();
b.position(23);
b.limit(42);

이것들은, 보다 편한 방식으로 한다면...다음과 같이 연결해서 코딩한다.

b.flip(). position(23). limit(42);

5. Buffer의 하위 클래스

버퍼류 클래스에는 boolean형을 제외한 나머지 기본형 데이터형에 맞는 Buffer 클래스들이 있다. 이들 클래스들은 각각의 데이터형에 맞는 여러 기능의 메서드를 가지고 있다. 또한 이들 하위 클래스들의 특징은 모두들 abstract 클래스인데 그 이유는 Heap Buffer와 Direct Buffer의 구분때문이다. 이들을 객체화 할때 allocate()메서드를 이용하는데 이때는 Heap Buffer를 사용한다. Direct Buffer를 사용하기 위해서는 ByteBuffer 클래스의 allocateDirect()메서드를 이용하는데 이는 ByteBuffer만 가능하다. 하지만 다른 유형의 버퍼로 변환하는 것은 가능하다. 따라서 버퍼류 클래스 객체 생성을 하는 allocate()메서드는 각각 클래스에 맞게 정의되어 있어서 그형의 클래스 객체를 리턴한다.

각 버퍼류 클래스마다 공통된 메서드가 거의 주류를 이루므로 한꺼번에 설명하겠다. 기능은 같으나 각 인자형이나 리턴형은 각 클래스에 따라 다르다라는 것을 알고 하나씩 살펴보자.

1> Buffer의 하위 클래스들의 주요 메서드

ㅁ allocate(int capacity)
:
각각의 버퍼 클래스형 객체 리턴(Heap Buffer)

ㅁ wrap(기본형 데이터 배열)
ㅁ wrap (기본형 데이터 배열, int offset, int length)
:
해당 기본형 데이터 유형 배열을 인자로 주면 이 배열이 해당 버퍼에 put되어서 클래스형 객체 리턴(Heap Buffer), 즉 버퍼생성과 동시에 데이터 저장이다.

ㅁ array()
: 현재 버퍼가 데이터를 저장하는데 쓰고 있는 기본형 데이터형의 배열을 리턴한다. 단 이 메서드는 hasArray() 메소드가 true를 리턴하는 경우에만 사용한다.hasArray() 메소드가 true를 리턴하려면 Direct Buffer가 아니며 데이터 관리를 배열로 하고 있는 경우여야 한다.

ㅁ abstract boolean isDirect ()
:
현재 버퍼가 Direct Buffer인지 아닌지를 리턴. Direct Buffer이면 true를 리턴한다.

ㅁ asReadOnlyBuffer()
: read 만 가능한 버퍼를 리턴.

ㅁcompact()
: 현재 position과 limit 사이에 남아있는 데이터들을 모두 버퍼 맨 앞쪽으로 이동시킨다. 그리고 position은 이들 데이터의 맨 마지막 칸으로 동하고 limit는 capacity와 같은 값을 가진다. 이메서드는 다른 메서드와는 달리 위치값뿐만아니라 데이터들의 이동이 있다는 점이 다르다.
예를 들어 다음 그림과 같이 현재 position이 4이고 limit가 7이라고 하자.

1
2
3
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9
(pos=4)
(limit=7)
(cap=10)

이 상태에서 compact()메서드를 호출하면 position과 limit 사이의 데이터들을 맨 앞으로 이동시키고 위치는 그 다음 칸으로 이동하고 limit는 capacity와 같은 값을 가진다.

5
6
7
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9
(pos=3)
(limit=cap=10)

ㅁ slice()
: 현재 position과 limit 의 범위를 별도의 버퍼로 만들어서 리턴해 준다. 즉 잘라내기라 생각하면 된다. 이것은 데이터는 공유하지만 position과 limit , mark값을 별도로 갖는 것으로 어느 한쪽에서 데이터를 수정하면 다른 한쪽도 수정한 데이터를 볼 수 있다. 이렇게 만들어진 새로운 버퍼는 position는 0으로, limit는 capacity와 같은 값을 가진다. mark는 재설정해 주어야 한다. 또 버퍼가 Heap Buffer라면 새로 생긴 버퍼도 Heap Buffer이며, 읽기전용이면 새 버퍼도 읽기전용이다.
예를 들어 다음 그림과 같은 버퍼를 slice()를 호출해서 새로 버퍼를 만든다면.. 그림과 같다.

1
2
3
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9
(pos=4)
(limit=7)
(cap=10)

5
6
7
0
1
2
<--새로 생긴 버퍼
(pos=0)
(limit=cap=10)

ㅁ duplicate()
: 데이터를 공유하는 또 하나의 버퍼를 생성해 주는데 이는 데이터 값만 복사되는 slice()와는 달리 position과 limit , mark 같은 모든 값이 그대로 복사된다. 즉 통째로 복사가 된다. 다만 복사된 이후에 position과 limit , mark값을 별도로 가진다. 그래서 내용만 공유하고 position과 limit , mark값는 따로 조작이 가능하다.
예를 들어 다음 그림과 같은 버퍼를 duplicate()를 호출해서 새로 버퍼를 만든다면.. 그림과 같다.

1
2
3
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9
(pos=4)
(limit=7)
(cap=10)

데이터를 공유하는 또 다른 버퍼 생성

1
2
3
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9
(pos=4)
(limit=7)
(cap=10)

2> 예제


import java.nio.*;

class SubBuffer {
public static void main(String[] args) {

System.out.println("-----------wrap()로 배열을 통째로 버퍼에 넣기 ");
int i[]={10,20,30,40,50,60,70,80,90,100};
// wrap()로 버퍼 생성과 동시에 데이터 put
IntBuffer buf=IntBuffer.wrap(i);
// 제대로 들어갔는지 데이터를 get해서 확인
while(buf.hasRemaining()){
System.out.print(buf.get()+",");
}
// position/limit/capacity확인
System.out.println("\n넣은후 : "+buf);

System.out.println("\n-----------array()로 반대로 버퍼를 배열로------");
// 버퍼의 데이터를 배열로 리턴, 이때 hasArray()가 true를 리턴해야 된다.
if(buf.hasArray()){
int a[]=buf.array();

// 배열 출력
for(int x=0;x<a.length;x++)
System.out.print(a[x]+",");
}
System.out.println("\n넣은후 : "+buf);

System.out.println("\n-----------compact()로 3부터 10까지 테이터를 맨앞으로------ ");
buf.position(3);
buf.compact();
System.out.println(buf);
for(int s=0;s<10;s++){
System.out.print(buf.get(s)+",");//출력
}

System.out.println("\n\n-----------duplicate()로 복사하기------- ");
IntBuffer buf2=buf.duplicate();
System.out.println("복사\n"+buf2);
buf.position(3);
System.out.println("원본 pos를 3으로 변경 :\n"+buf);
System.out.println("복사본은 ?:\n"+buf);
System.out.println("복사본 내용--");
for(int s=0;s<10;s++){
System.out.print(buf2.get(s)+",");//출력
}

System.out.println("\n\n-----------slice()로 자르기 ------");
IntBuffer buf3=buf.slice();
System.out.println("자르기\n"+buf3);
buf.position(3);
System.out.println("\n원본 pos를 3으로 변경 :\n"+buf);
System.out.println("자른 pos은 ?:\n"+buf3);
System.out.println("자른본 내용--");
while(buf3.hasRemaining()){
System.out.print(buf3.get()+",");//출력
}

System.out.println("\n\n-----------원본 데이터를 바꾼다. ------");
buf.clear();
for(int s=0;s<5;s++){
buf.put(s);//0에서 4까지 버퍼에 저장
}
System.out.println("\n바뀐 원본 : ");
for(int s=0;s<10;s++){
System.out.print(buf.get(s)+",");//출력
}

buf2.clear();
System.out.println("\n복사본은? : ");
for(int s=0;s<10;s++){
System.out.print(buf2.get(s)+",");//출력
}

buf3.clear();
System.out.println("\n자른본은? : ");
while(buf3.hasRemaining()){
System.out.print(buf3.get()+",");//출력
}
}
}

<< 실행 결과 >>


C\>java SubBuffer

-----------wrap()로 배열을 통째로 버퍼에 넣기
10,20,30,40,50,60,70,80,90,100,
넣은후 : java.nio.HeapIntBuffer[pos=10 lim=10 cap=10]

-----------array()로 반대로 버퍼를 배열로------
10,20,30,40,50,60,70,80,90,100,
넣은후 : java.nio.HeapIntBuffer[pos=10 lim=10 cap=10]

-----------compact()로 3부터 10까지 테이터를 맨앞으로------
java.nio.HeapIntBuffer[pos=7 lim=10 cap=10]
40,50,60,70,80,90,100,80,90,100,

-----------duplicate()로 복사하기-------
복사
java.nio.HeapIntBuffer[pos=7 lim=10 cap=10]
원본 pos를 3으로 변경 :
java.nio.HeapIntBuffer[pos=3 lim=10 cap=10]
복사본은 ?:
java.nio.HeapIntBuffer[pos=3 lim=10 cap=10]
복사본 내용--
40,50,60,70,80,90,100,80,90,100,

-----------slice()로 자르기 ------
자르기
java.nio.HeapIntBuffer[pos=0 lim=7 cap=7]
원본 pos를 3으로 변경 :
java.nio.HeapIntBuffer[pos=3 lim=10 cap=10]
자른 pos은 ?:
java.nio.HeapIntBuffer[pos=0 lim=7 cap=7]
자른본 내용--
70,80,90,100,80,90,100,

-----------원본 데이터를 바꾼다. ------
바뀐 원본 :
0,1,2,3,4,90,100,80,90,100,
복사본은? :
0,1,2,3,4,90,100,80,90,100,
자른본은? :
3,4,90,100,80,90,100,

소스가 좀 길다. 그 이유는 확인을 하기위한 출력문이 많아서 그런것 뿐 이런것들을 생략하면 반으로 줄어든다. 자 하나씩 그림과 함께 살펴보자.

1. 버퍼를 wrap() 로 생성과 동시에 배열을 데이터로 put()

10
20
30
40
50
60
70
80
90
100
0
1
2
3
4
5
6
7
8
9
(pos=0)
(limit=cap=10)

2. array()메서드로 버퍼에 저장된 데이터를 배열로 리턴 받는데 반드시 hasArray()가 true를 리턴해야 된다.

3. 우선 position(3)으로 버퍼의 현 위치를 3으로 설정한 다음 campact()메서드를 호출한다. 그러면 위치 3에서 limit가 10이므로 이 사이의 데이터 7개가 버퍼 맨 앞으로 이동하고 위치(pos)는 7로 이동, limit는 capacity와 같은 값을 가진다.

40
50
60
70
80
90
100
80
90
100
0
1
2
3
4
5
6
7
8
9
(pos=7)
(limit=cap=10)

4. duplicate() 메서드를 호출, 버퍼를 복사해서 새로운 버퍼를 buf2에 담는다. 이때 pos/limit/cap 값들이 그대로 복사됨을 알 수 있다.

40
50
60
70
80
90
100
80
90
100
0
1
2
3
4
5
6
7
8
9
<- buf(원본)
(pos=7)
(limit=cap=10)

40
50
60
70
80
90
100
80
90
100
0
1
2
3
4
5
6
7
8
9
<- buf2(복사본)
(pos=7)
(limit=cap=10)

5. 복사를 하고 나서 원본인 buf의 position을 3으로 바꾸어 보니 복사본인 buf2도 position가 3으로 바뀐것을 확인 할 수 있다.

6. 이번에도 버퍼를 하나 더 생성하는데 slice()를 이용해서 새로 생긴 버퍼를 buf3에 담든다.그럼 지금 원본인 buf의 pos가 3이고 limit가 10이므로 이 범위의 데이타로 buf3를 생성하므로 다음과 같은 그림이다.

40
50
60
70
80
90
100
80
90
100
0
1
2
3
4
5
6
7
8
9
<- buf(원본)
(pos=3)
(limit=cap=10)

70
80
90
100
80
90
100
0
1
2
3
4
5
6
<- buf3(자른본)
(pos=0)
(limit=cap=7)

7. 역시 여기서도 원본인 buf의 position을 3으로 바꾸었다. 하지만 복사본과는 달리 자르기를 한 buf3는 전혀 변화가 없음을 알 수 있다.

8. 이번에는 원본의 데이터를 바꾸면 복사본과 자른 버퍼의 데이터도 바뀌는 지를 알아본다. 우선 원본인 buf의 데이터의 위치 0번부터 5번까지 데이터를 0에서 4로 바꾸었다. 그리고 나서 복사본과 자른본의 데이터를 출력하니 역시 데이터가 바뀐것을 알 수있다. 이는 복사본과 자른본은 원본과 데이터를 공유한다는 것을 알 수있다. 하지만 복사본은 원본과 pos나 limit값들도 함께 공유하지만 자른본은 데이터만 공유함을 알 수 있다.

조금 복잡해 보이나 그림을 그려가면서 따져보면 이해할 수 있다. 자 그럼 버퍼류클래스들을 살펴볼텐데 대부분이 같은 기능을 가진다. 그중에서 추가 기능을 가진 ByteBuffer와 CharBuffer에 대해서 알아보자.

6. ByteBuffer 클래스

우선 api문서에 있는 ByteBuffer 클래스의 선언은 다음과 같다.


public abstract class ByteBuffer extends Buffer implements Comparable

선언부분을 보면 ByteBuffer 클래스는 Buffer 클래스를 상속하고 Comparable 인터페이스를 구현하고 있다. Buffer 클래스를 상속는 누누히 말해 온 것이고 Comparable 인터페이스를 구현한다는 것은 ByteBuffer 클래스가 compareTo()라는 메서드를 가지고 있기 때문이다. 이는 다른 Buffer 클래스의 하위 클래스도 다 마찬가지이다.

ByteBuffer 클래스의 특징은 다음과 같이 두가지로 들수 있다.

  1. Direct Buffer를 만들 수 있다.
  2. 다른 유형의 버퍼로 변환이 가능하다.

이는 사실 하나로 생각될 수 있는데 그것은 ByteBuffer 클래스가 Direct Buffer를 만들면 이 Direct Buffer를 다른 유형으로 변환해서 쓸 수 있기 때문이다.

1> ByteBuffer 클래스 추가 메서드

ㅁ public static ByteBuffer allocateDirect(int capacity)
: Direct Buffer를 생성한다. Direct Buffer는 자바가 제공하는 Heap 메모리를 쓰지 않고 운영체제가 제공하는 메모리를 사용하므로 만들때에는 시간이 걸린다. 하지만 입출력 속도가 매우 빠른 장점을 가지고 있다. 따라서 Direct Buffer는 대용량의 데이터를 다루고 오랜 기간동안 쓰면서 빠른속도가 필요한 곳에 적합하다. 또 다른 장점으로는 JNI를 통해 직접 접근이 가능하다. JNI는 native 코드이므로 Direct Buffer를 navtive 코드에 쓰거나 native 에서 수정한 코드를 자바에서 쓰는 것이 가능하다.

ㅁ 다른 유형으로 변환시키는 메서드 -> asXXXBuffer()

public abstract CharBuffer asCharBuffer()
public abstract DoubleBuffer asDoubleBuffer()
public abstract FloatBuffer asFloatBuffer()
public abstract IntBuffer asIntBuffer()
public abstract LongBuffer asLongBuffer()
public abstract ShortBuffer asShortBuffer()

이들 메서드로 변환된 ByteBuffer는 변환유형에 맞는 Buffer로서의 기능을 할 수 있게 된다.

ㅁ public final ByteOrder order()
: 해당 버퍼의 ByteOrder를 리턴

ㅁ public final ByteBuffer order(ByteOrder bo)
:
인자로 들어온 ByteOrder로 버퍼를 설정한다. 인자로는 바이트 순서인 BIG_ENDIAN 또는 LITTLE_ENDIAN 이 온다.
: 다른 클래스에서도 ByteOrder(바이트 순서)를 변경할 수 있는데, 이는 직접 변경할 수는 없고 ByteBuffer클래스에서 바꾼 다음 이른 변환해야 한다.

ㅁ getXXX()/putXXX()
: ByteBuffer는 여러 유형의 데이터를 put/get 할 수 있는 메서드가 있다. put/get 뒤에 해당 데이터형만 적으면 된다. 이는 ByteBuffer가 다른 유형의 버퍼로 전환될 수 있는 능력을 가지고 있기 때문이다. 예를 들어 다음과 같이 사용한다.

buf1.putChar('가');
buf1.putDouble(3.14);
buf1.putFloat(3.14f);

이를 다음과 같이 호출해도 상관없다.

buf1.putChar('가').putDouble(3.14).putFloat((float)3.14);

ㅁ public int compareTo(Object ob)
: 인자로 들어온 객체 ob와 비교해서 결과 값을 리턴해 준다. 이때 리턴값이 음수면 객체 ob보다 자신이 작다는 뜻이고, 0은 같다는 뜻, 양수는 객체 ob보다 자신이 크다는 뜻이다. 주의할 점은 반드시 유형이 같은 버퍼끼리 비교해야 한다.

2> 예제


import java.nio.*;

public class ByteBufferTest {
public static void main(String[] args) {
// 버퍼를 생성한다.
ByteBuffer buf1=ByteBuffer.allocate(25);
// ByteOder(데이터 순서)를 출력한다.
System.out.println("Byte order : "+buf1.order());
// 여러 유형의 데이터를 put한다.
buf1.putChar('가');
buf1.putDouble(3.14);
buf1.putFloat((float)3.14);
// buf1.putChar('가').putDouble(3.14).putFloat((float)3.14); 라고 해도 된다.

System.out.println("buf1 : "+buf1);

// 여러 유형의 데이터를 get한다.
System.out.println(buf1.getChar());
System.out.println(buf1.getDouble());
System.out.println(buf1.getFloat());

//새로운 버퍼 생성
ByteBuffer buf2=ByteBuffer.allocate(20);
// 데이터 int형을 put
for(int i=1;i<=5;i++){
buf2.putInt(i);
}
// 버퍼의 위치값을 0으로 설정
buf2.clear();
// ByteBuffer를 IntBuffer로 변환시킨다.
IntBuffer intbuf=buf2.asIntBuffer();

System.out.println("IntBuffer로 변환후 값은:"+intbuf);

while(intbuf.hasRemaining()){
System.out.println(intbuf.get());//출력

}

}
}

<< 실행 결과 >>


C\>java ByteBufferTest

Byte order : BIG_ENDIAN
buf1 : java.nio.HeapByteBuffer[pos=14 lim=25 cap=25]

3.14
3.14
IntBuffer로 변환후 값은:java.nio.ByteBufferAsIntBufferB[pos=0 lim=5 cap=5]
1
2
3
4
5

소스를 보면 ByteBuffer 를 생성하고 이것의 ByteOrder를 출력해보니 BIG_ENDIAN임을 확인할 수 있다. 이것은 운영체계와 별개의 값이다. 자바가상머신은 하나의 독립된 컴퓨터이기 때문이다. 그리고 char,double,float 형의 데이터를 putXXX()메서드로 넣고 있다. 그리고 나서 다시 get을 해서 출력한다.

다시 크기가 20인 바이트 버퍼를 생성하고 이번에는 int형의 데이터를 삽입했다. 그리고 나서 버퍼의 위치를 clear()메서드로 0으로 만들고 나서 ByteBuffer를 IntBuffer로 변환한다. 변환하기전에 반드시 버퍼의 위치를 0으로 만들주어야 한다. 그이유는 변환시 버퍼의 현재 위치부터 변환이 시작되기 때문이다. 변환된 객체 intbuf를 출력하면 position는 0이 되고 limit와 capacity가 5으로 바뀐것을 볼 수 있다. 이는 IntBuffer로 변환되면서 데이터 크기를 int형의 크기로 바꾸어 버리기 때문이다. 즉 총 크기가 20이니 int는 4바이트를 차지하니깐 이를 나누면 5이 나온다. 하지만 데이터는 int형이 아니라서 그 크기에 맞게 들어간 것이 보인다.

7. CharBuffer 클래스

CharBuffer는 char 데이터 유형을 대상하는 버퍼로 특징은 1.4부터 추가된 java.lang.CharSequence 인터페이스를 구현하고 있다는 점이다. 다음은 CharBuffer의 클래스 선언문이다.


public abstract class CharBuffer extends Buffer implements Comparable , CharSequence

CharSequence 인터페이스를 구현하기 때문에, 문자 순서를 받아들일 수 있는 장소이면 어디에서라도, char 버퍼를 사용할 수 있다. 예를 들어, 1.4부터 추가된 정규 표현의 패키지 java.util.regex 에서의 사용이 가능하다. 또한 wrap()메서드에서 char[] 배열 대신에 CharSequence 객체가 들어 올 수 있어서 String이나 StringBuffer를 wrap()해서 CharBuffer로 만들 수 있다.

CharSequence 인터페이스

JDK 1.4부터 추가된 인터페이스로서 CharSequence 는 읽어들일 수 있는 문자 순서를 나타낸다. 이 인터페이스는, 다양한 종류의 문자 순서에의 통일된 read 전용 액세스를 제공한다.

<< 메서드 >>
ㅁ char charAt (int index) : 지정된 인덱스 위치에 있는 문자를 리턴
ㅁ int length () : 이 문자 순서의 길이를 리턴.
ㅁ CharSequence subSequence (int start, int end) : 이 순서의 서브 순서인 신규 문자 순서를 리턴.
ㅁ String toString () : 이 순서와 같은 순서로 문자를 포함한 스트링을 리턴.

구현 클래스 : CharBuffer , String , StringBuffer

모든 버퍼류 클래스의 객체들은 toString()메서드 호출시 자신의 여러 설정들을 보여준다. 가령 예를 들어 다음과 같이 코드를 작성해서 실행을 했다고 하자.

IntBuffer buf=IntBuffer.allocate(10);
System.out.println("생성된 후:"+buf);//자동 toString() 호출....당연,,,

==>실행 결과
생성된 후 : java.nio.HeapIntBuffer[pos=0 lim=10 cap=10]

하지만 CharBuffer는 자신이 가지고 있는 char들을 보여준다. 따라서 CharBuffer의 여러 값들을 보고자 할때에는 메서드를 사용해야 한다.CharBuffer에는 위같은 사실만 알고 있으면 사용하는데는 별 무리가 없다.

1> 예제


import java.nio.*;

public class CharBufferTest {
public static void main(String[] args) {

// CharBuffer에 String 문자열로 wrap한다.
String s="String 문자열";
CharBuffer buf=CharBuffer.wrap(s);
// 버퍼의 값들을 알기 위해서는 다른 클래스와는 달리 메서드를 사용해야 한다.
System.out.println("pos:"+buf.position()+" limit:"+buf.limit()+" cap:"+buf.capacity());
buf.clear();
while(buf.hasRemaining()){
System.out.print(buf.get());//출력
}

System.out.println("\n"+"-------------------------");

// CharBuffer에 StringBuffer 문자열로 wrap한다.
StringBuffer s2 = new StringBuffer("StringBuffer 문자열");
CharBuffer buf2=CharBuffer.wrap(s2);
System.out.println("pos:"+buf2.position()+" limit:"+buf2.limit()+" cap:"+buf2.capacity());
buf2.clear();
while(buf2.hasRemaining()){
System.out.print(buf2.get());
//출력
}
}
}

<< 실행 결과 >>


C\>java CharBufferTest

pos:0 limit:10 cap:10
String 문자열
-------------------------
pos:0 limit:16 cap:16
StringBuffer 문자열

소스를 보면 두 개의 CharBuffer를 생성해서 하나는 String 을 데이터로, StringBuffer를 데이터로 삽입한다. 이것이 가능한 이유는 앞서 말했지만 CharSequence 인터페이스를 구현하기 때문이다. 그리고 CharBuffer의 객체를 출력시 값을 알기위해서 다음과 같이 출력함을 볼 수 있다.

System.out.println("pos:"+buf.position()+" limit:"+buf.limit()+" cap:"+buf.capacity());

[출처] Nio Buffer정리[펌]|작성자 짱가

 

.
:
Posted by .07274.
2013. 4. 18. 14:56

[펌] 제한자 정리 I.lib()/I.lib(Java)2013. 4. 18. 14:56

.. .. ..

[출처] 제 2장 6.7. native/ transient/ synchronized/ volatile 제한자와 위치|작성자 멋진놈

 

이절에서 소개하는 제한자는 자주 사용되지는 않는다. 하지만 이 제한자들이 어떤 용도로 사용되는지에 대해서는 알으시면 될것 같습니다. 이 제한자들 또한 자바 키워드임을 명심하자.

native 제한자
- native 제한자(modifier)는 자바가 아닌 다른 언어로 작성된 코드를 자바에서 사용하기 위한 것이다.
이제한자는 반드시 메소드에만 선언되어야 한다.

transient 제한자
- transient 제한자는 객체가 직렬화되는 과정에서 해당 필드가 저장되지 않아야 한다는 것을 알리기
위해 사용된다. 이 제한자는 반드시 멤버 변수에만 선언되어야 한다.

synchronized 제한자
- synchronized 제한자는 코드의 같은 블록을 하나 이상의 스레드가 동시에 접근하는 것을 막기 위
해 사용된다. 이 제한자는 반드시 메소드나 블록에 선언되어야 한다.

volatile 제한자
- volatile 제한자는 변수가 스레드에 의해 비동기적으로 변경될 수 있음을 알리기 위해 사용된다. 이
제한자는 final 변수를 제외한 변수에 선언될 수 있다.

제한자들은 선언될 수 있는 위치가 정해져 있기 때문에 선언 기능 위치에 대해서 정확히 알고 선언하는 것이 중요하다.
제한자
변수
메소드
클래스
내부 클래스
public
private
×
protected
×
final
abstract
×
static
×
native
×
×
×
transient
×
×
×
volatile
×
×
×
synchronized
×
×
×


※ 제한자의 선언 위치

 

.
:
Posted by .07274.
2013. 4. 9. 11:48

[펌] Log4j 설정 (xml 파일) I.lib()/I.lib(Java)2013. 4. 9. 11:48

.. .. ..

[펌] : http://rahxephon.tistory.com/1460

Logging Service

개발자가 Log을 출력하기 위해 일반적으로 사용하는 방식은 System.out.println()이다. 그러나 이 방식은 간편한 반면에 다음과 같은 이유로 권장하지 않는다.

  • System.out.println에 대한 호출은 disk I/O동안 동기화(synchronized)처리가 되므로 시스템의 throughput을 떨어뜨린다.
  • 기본적으로 stack trace 결과는 콘솔에 남는다. 하지만 시스템 운영중 콘솔을 통해 Exception을 추적하는 것은 바람직하지 못하다.
  • 운영시스템에서 시스템 관리자가 System.out과 system.errs에 대하여 ‘[>null]’(NT의 경우) 혹은 dev/null(Unix의 경우)와 같이 매핑을 할 경우 Exception 로그에 대한 출력이 나타나지 않을 수도 있다. 또한 NT 서비스로 실행될 경우 콘솔 자체가 안보일 수 도 있다.
  • 콘솔 로그를 출력 파일로 리다이렉트 할 지라도, J2EE App Server가 재 시작할 때 파일이 overwrite될 수도 있다.
  • 개발/테스팅 시점에만 System.out.println을 사용하고 운영으로 이관하기 전에 삭제하는 것은 좋은 방법이 아니다. 운영시의 코드가 테스트시의 코드와 다르게 동작할 수 있기 때문이다.

따라서, 테스팅 코드와 운영 코드를 동일하게 가져가면서 로깅을 선언적으로 관리할 수 있고, 운영시 성능 오버헤드를 최소화할 수 있는 메커니즘이 필요하다. 이런 기능을 위해 Anyframe Framework은 Log4j 를 이용하여 로그를 남길 수 있는 방법을 가이드하고자 한다.

Logging Service Configuration 정의하기

이번 절에서는 log4j.xml 파일을 구성하는 Tag 중, 주로 사용될 일부 Tag에 대해 설명하고자 한다. 보다 자세한 내용에 대해서는 Log4j 를 참조하도록 한다. log4j.xml 파일의 root tag인 <log4j:configuration>은 하위에 appender, logger, root등의 tag를 가질 수 있다.
Tag 명
설명
필수 여부
appender 로그가 출력될 대상과 방법을 정의한다. 여러 appender 정의 가능.
N
logger 어플리케이션에서 사용될 Logger를 정의한다. 여러 logger 정의 가능
N
root 모든 logger의 상위 logger를 정의한다.
N

위 표에서 열거한 각 Tag에 대해 보다 자세히 알아보도록 하자.

appender

Log4j는 다양한 로그 방식을 지원한다. 가장 단순한 Console부터 시작해서 파일, DB, SMTP 등의 방식들을 지원한다.
  • org.apache.log4j.ConsoleAppender : Console 화면으로 출력하기 위한 Appender. 다음은 log4j.xml 파일 내의 ConsoleAppender에 대한 속성 정의 내용이다.
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %p [%c] - &lt;%m&gt;%n" />
        </layout>   
    </appender>
  • org.apache.log4j.FileAppender : 특정 파일에 로그를 출력하기 위한 Appender로 하위에 File, Append와 같은 parameter를 정의할 수 있다. 다음은 log4j.xml 파일 내의 FileAppender에 대한 속성 정의 내용이다.
    <appender name="file" class="org.apache.log4j.FileAppender">
    	<!-- 로그 파일명 정의를 위한 parameter -->
    	<param name="File" value="./logs/file/sample.log"/>
    	<!-- 이전 로그 파일에 로그를 덧붙여 쓸 것인지를 정의하기 위한 parameter -->
    	<param name="Append" value="true"/>    	  
    	<layout class="org.apache.log4j.PatternLayout">
    	    <param name="ConversionPattern" value="%d %p [%c] - &lt;%m&gt;%n" />
    	</layout>   
    </appender>
  • org.apache.log4j.RollingFileAppender : FileAppender는 지정한 파일에 로그가 계속 남으므로 한 파일의 크기가 지나치게 커질 수 있으며 계획적인 로그 관리가 어렵다. 따라서 파일의 크기 또는 파일 백업 인덱스 등의 지정을 통해 특정 크기 이상 파일의 크기가 커지게 되면 기존 파일을 백업 파일로 바꾸고 처음부터 다시 로깅을 시작한다. 하위에 File, Append, MaxFileSize, MaxBackupIndex와 같은 parameter를 정의할 수 있다. 다음은 log4j.xml 파일 내의 RollingFileAppender에 대한 속성 정의 내용이다.
    <appender name="rollingFile" class="org.apache.log4j.RollingFileAppender">
    	<!-- 로그 파일명 정의를 위한 parameter -->
    	<param name="File" value="./logs/rolling/sample.log"/>
    	<!-- 이전 로그 파일에 로그를 덧붙여 쓸 것인지를 정의하기 위한 parameter -->
    	<param name="Append" value="true"/> 
    	<!-- 로그 파일의 최대 크기를 정의하기 위한 parameter -->   	  
    	<param name="MaxFileSize" value="1KB"/>    
    	<!-- 로그 파일 백업 인덱스를 정의하기 위한 parameter -->
    	<param name="MaxBackupIndex" value="2"/>    
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %p [%c] - &lt;%m&gt;%n" />
        </layout>   
    </appender>
  • org.apache.log4j.DailyRollingFileAppender : 설정한 날짜 또는 조건에 맞춰 로깅을 수행하기 위한 Appender. DailyRollingFileAppender 파일에서 사용할 수 있는 몇가지 날짜 포맷은 다음과 같다. 하위에 File, DatePattern과 같은 parameter를 정의할 수 있다.
    • .yyyy-MM : 매달 첫번째 날에 로그파일 변경
    • .yyyy-ww : 매주 시작시 로그파일 변경
    • .yyyy-MM-dd : 매일 자정에 로그파일 변경
    • .yyyy-MM-dd-a : 자정과 정오에 로그파일 변경
    • .yyyy-MM-dd-HH : 매시간의 시작마다 로그파일 변경
    • .yyyy-MM-dd-HH-mm : 매분마다 로그파일 변경
    보다 자세한 사항은 Log4j API 를 참조한다.
    다음은 log4j.xml 파일 내의 DailyRollingFileAppender에 대한 속성 정의 내용이다.
    <appender name="dailyRollingFile" class="org.apache.log4j.DailyRollingFileAppender">
    	<!-- 로그 파일명 정의를 위한 parameter -->
    	<param name="File" value="./logs/daily/sample.log"/>
    	<!-- 로그 파일을 Rolling하기 위한 날짜 조건을 정의하기 위한 parameter -->
    	<param name="DatePattern" value=".yyyy-MM-dd-HH-mm"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %p [%c] - &lt;%m&gt;%n" />
        </layout>   
    </appender>
  • org.apache.log4j.jdbc.JDBCAppender : DB에 로그를 출력하기 위한 Appender로 하위에 Driver, URL, User, Password, Sql과 같은 parameter를 정의할 수 있다. 다음은 log4j.xml 파일 내의 JDBCAppender에 대한 속성 정의 내용이다.
    <appender name="db" class="org.apache.log4j.jdbc.JDBCAppender">
    	<!-- JDBC Driver를 정의하기 위한 parameter -->
    	<param name="Driver" value="oracle.jdbc.driver.OracleDriver"/>
    	<!-- DB URL을 정의하기 위한 parameter -->
    	<param name="URL" value="jdbc:oracle:thin:@107.108.150.108:1521:ora10"/>
    	<!-- DB User를 정의하기 위한 parameter -->
    	<param name="User" value="anyframe"/>
    	<!-- DB Password를 정의하기 위한 parameter -->
    	<param name="Password" value="anyframe"/>
    	<!-- 로그를 남길때 수행할 쿼리를 정의하기 위한 parameter -->
    	<param name="Sql" value="insert into STMR_LOG (msg)
    	                        values('%d %p [%c] - &lt;%m&gt;%n')"/>
    </appender>
Appender Layout
로그를 남길때 단순한 메시지 외에도 현재 로그 대상의 쓰레드명, 로그 시간 등 많은 정보들을 조합할 수 있다. Layout에는 org.apache.log4j.HTMLLayout, org.apache.log4j.PatternLayout, org.apache.log4j.SimpleLayout, org.apache.log4j.xml.XMLLayout 등이 있다. 이중 가장 많이 사용하는 Layout은 PatternLayout으로서 C 함수의 printf처럼 다양한 로그 메시지 조합을 만들어 낼 수 있다.
  • %p : debug, infor, warn, error, fatal 등의 Priority 출력
  • %m : debug(), info(), warn(), error(), fatal() 등의 함수로 지정한 로그 내용 출력
  • %d : 로깅 이벤트가 발생한 시간 기록. 출력 포맷은 %d 후의 {}내에 지정된 형태를 따른다. %d{HH:mm:ss, SSS} 라든가 %d{yyyy MMM dd HH:mm:ss, SSS}와 같은 형태로 사용할 수 있다. Java의 SimpleDateFormat의 형식을 따라 정의할 수 있다.
  • %t : 로깅 이벤트가 발생된 쓰레드의 이름 출력
  • %% : % 표시를 출력하기 위해 사용
  • %n : 플랫폼 종속적인 개행 문자 출력. \r\n 또는 \n이 될 것이다.

logger

로깅 이벤트 발생시 같은 이름으로 선언된 logger를 찾아 해당 logger에게 로그 메시지를 보내고 additivity가 true일 경우, 상위 logger에게도 로그 메시지를 보낸다. 다음은 log4j.xml 파일 내의 logger에 대한 속성 정의 내용이다.
<!-- 해당 logger명이 anyframe.services로 시작할 경우 Console에 DEBUG level로 로그를 남긴다.-->
<logger name="anyframe.services">
	<!-- DEBUG, INFO, WARN, ERROR, FATAL, OFF 중 택일 -->
	<level value="DEBUG"/>
	<!-- 여러 appender-ref 정의 가능 -->
	<appender-ref ref="console"/>
</logger>

root

해당 logger가 존재하지 않거나 상위 logger가 존재하지 않을 경우 모든 로그는 root logger의 정책에 따라 출력된다. 다음은 log4j.xml 파일 내의 root에 대한 속성 정의 내용이다.
<root>
    <level value="INFO"/>
    <appender-ref ref="console"/>
</root>

Logging Service 사용하기

로그의 내용에 따라 다양한 레벨(DEBUG, INFO, WARN, ERROR, FATAL)로 선택 가능하다. 각각은 메소드 debug(), info(), warn(), error(), fatao()라는 5가지 메소드를 이용해서 로그를 남길 수 있다. 다만 이때 Logger에 지정된 로그 레벨이 있다면, 지정된 로그 레벨 이하의 로깅 이벤트는 무시된다. 따라서 로그도 남지 않는다. 또한, 로그 메시지는 별도 Resource 파일에 정의된 message key를 이용하여 남기면 메시지 변경 및 다국어 지원이 용이하다. 다음에서는 로그 메시지를 남기기 위한 기본 방법ResourceBundle을 이용하는 방법 에 대해서 알아보고자 한다.

기본적인 사용 방법

다음은 기본적인 방법을 사용하여 로그 메시지를 남기는 LoggingServiceTest.java 코드의 일부이다.
/**
 * resources/test/resources/common/conf/log4j.xml 파일 설정에 따라 Logger명이
 * integration.anyframe.services.logging.LoggingServiceTest인 Logger를 찾고, 해당
 * Logger를 통해 로그 메시지를 남겨보고 해당 Logger의 레벨이 DEBUG 가능한지 체크해보는 테스트
 */
public void testLogging() throws Exception {
	Log logger = LogFactory.getLog(LoggingServiceTest.class);
	logger.debug("Sample DEBUG Message.");
	logger.info("Sample Info Message.");
	logger.warn("Sample Warning Message.");
	logger.error("Sample Error Message.");
	logger.fatal("Sample Fatal Message.");
	if (!logger.isDebugEnabled())
		throw new Exception("fail to get Logger");
}

ResourceBundle을 이용하는 방법

특정 서비스의 구현 클래스에서 ResourceBundle을 이용하여 로그 메시지를 남기기 위해서는 다음과 같은 절차를 따르도록 한다.
1. ResourceBundle을 관리하는 기능을 제공하는 MessageSource Bean을 정의한다.
다음은 MessageSource Bean을 정의하고 있는 applicationContext-common.xml 파일의 일부이다.
<bean name="messageSource" 
    class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <!-- 중략 -->
            <value>
               classpath:/message/message-sample
            </value>
        </list>
        <!-- 중략 -->
    </property>
</bean>
2. 특정 서비스의 구현 클래스는 MessageSource Bean을 인식하기 위하여 implements ApplicationContextAware해야 한다.
다음은 MessageSource Bean을 이용하여 로그 메시지를 남기는 LoggingSampleServiceImpl.java 의 일부 코드이다.
public class LoggingSampleServiceImpl implements LoggingSampleService,
		ApplicationContextAware {
	private MessageSource messageSource = null;
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.messageSource = (MessageSource) applicationContext
				.getBean("messageSource");
	}
	
	// 중략
}
3. Logging Service를 이용하여 로그를 남길 때 MessageSource Bean을 이용한다.
다음은 Service 구현 클래스에서 사용할 Logger를 정의한 인터페이스 클래스 LoggingSampleService.java 코드이다.
public interface LoggingSampleService {
	String ROLE = LoggingSampleService.class.getName();
	// LoggingSampleServiceImpl에서 사용할 Logger 정의
	Log LOGGER = LogFactory.getLog(LoggingSampleService.class);
	public String greet();
}
다음은 ResourceBundle을 이용하여 로그 메시지를 남기는 구현 클래스 LoggingSampleServiceImpl.java 의 일부 코드이다.
public String greet() {
	// ResourceBundle을 이용하여 로그 메시지를 남긴다. (argument가 없는 경우)
	LoggingSampleService.LOGGER.debug(messageSource.getMessage(
			"sample.default.msg", new String[] {}, Locale.getDefault()));
	// ResourceBundle을 이용하여 로그 메시지를 남긴다. (argument가 1개인 경우)
	LoggingSampleService.LOGGER.debug(messageSource.getMessage(
			"sample.msg", new String[] { "GilDong" }, Locale.getDefault()));
	return "Hello";
}
* 위의 코드에서 참조하고 있는 Resource 파일 message-sample.properties 의 내용은 다음과 같이 key=value 형태로 정의한다.
sample.default.msg=Hello Guest
sample.msg=Hello {0}
.
:
Posted by .07274.
.. .. ..

[펌] : http://blog.naver.com/PostView.nhn?blogId=10353&logNo=10022595461&redirect=Dlog&widgetTypeCall=true

 

DBCP(DataBase Connection Pool)의 사용 - Java Application
<BASE target="_son">

DBCP(DataBase Connection Pool)의 사용 - Java Application
[01] DBCP(DataBase Connection Pool)의 사용
- 다운로드:
http://jakarta.apache.org
- DBCP: DBCP 구현 Liarary
http://jakarta.apache.org/site/downloads/downloads_commons-dbcp.cgi
commons-dbcp-1.2.1.jar
- Commons Collections: Collection Framework 구현
http://jakarta.apache.org/site/downloads/downloads_commons-collections.cgi
commons-collections-3.1.jar
- Commons Pool: 객체 버퍼링 라이브러리 구현
http://jakarta.apache.org/site/downloads/downloads_commons-pool.cgi
commons-pool-1.2.jar
1. DBCP구현
>>>>> DBCPConnectionMgr.java
package phonecalc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDriver;
import org.apache.commons.pool.impl.GenericObjectPool;
public class DBCPConnectionMgr {
String jdbcDriver="oracle.jdbc.driver.OracleDriver";
String jdbcURL="jdbc:oracle:thin:@172.16.11.1:1521:ora10g2";
String user="ejb2030_04_1";
String password = "oracle";

/**
* 생성자
*
*/
public DBCPConnectionMgr() {
super();
try{
setupDriver(jdbcDriver, jdbcURL, user, password);
}catch(Exception ex){
ex.printStackTrace();
}
}
/**
* Connection을 리턴함
* @return
*/
public Connection getConnection(){
Connection con = null;
try {
con = DriverManager.getConnection("jdbc:apache:commons:dbcp:ora10g2");
} catch(SQLException ex) {
ex.printStackTrace();
} finally {
}
return con;
}

/**
* Connection 설정
* @param jdbcDriver
* @param jdbcURL
* @param user
* @param password
* @throws Exception
*/
public void setupDriver(String jdbcDriver,
String jdbcURL,
String user,
String password) throws Exception {
// JDBC 드라이버 로딩
Class.forName(jdbcDriver);

// Connection Pool 생성
GenericObjectPool connectionPool = new GenericObjectPool(null);
connectionPool.setMaxActive(20); //최대 20개 접속 지원
connectionPool.setMaxIdle(5); //5개 대기중 설정

// 실제 DB와의 커넥션을 연결해주는 팩토리 생성
ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
jdbcURL, // JDBC URL
user, // 사용자
password);

// Connection Pool이 PoolableConnection 객체를 생성할 때 사용할
// PoolableConnectionFactory 생성
PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(
connectionFactory,
connectionPool,
null, // statement pool
null, // 커넥션 테스트 쿼리: 커넥션이 유효한지 테스트할 때 사용되는 쿼리.
false, // read only 여부
true); // auto commit 여부

// Pooling을 위한 JDBC 드라이버 생성 및 등록
PoolingDriver driver = new PoolingDriver();

// JDBC 드라이버에 커넥션 풀 등록
driver.registerPool("ora10g2", connectionPool);
}

public void freeConnection(Connection con, PreparedStatement pstmt, ResultSet rs) {
try {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
freeConnection(con);
} catch (SQLException e) {
e.printStackTrace();
}
}
public void freeConnection(Connection con, Statement stmt, ResultSet rs) {
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
freeConnection(con);
} catch (SQLException e) {
e.printStackTrace();
}
}

public void freeConnection(Connection con, PreparedStatement pstmt) {
try {
if (pstmt != null) pstmt.close();
freeConnection(con);
} catch (SQLException e) {
e.printStackTrace();
}
}
public void freeConnection(Connection con, Statement stmt) {
try {
if (stmt != null) stmt.close();
freeConnection(con);
} catch (SQLException e) {
e.printStackTrace();
}
}

public void freeConnection(Connection con) {
try {
if (con != null) con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

public void freeConnection(Statement stmt) {
try {
if (stmt != null) stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

public void freeConnection(PreparedStatement pstmt) {
try {
if (pstmt != null) pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void freeConnection(ResultSet rs) {
try {
if (rs != null) rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

}

2. DBCP JAVA Test
>>>>> DBCP_Test.java
package test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import phonecalc.DBCPConnectionMgr;
public class DBCP_Test {

public static void main(String[] args) {
DBCPConnectionMgr dbcp = new DBCPConnectionMgr();
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql="SELECT count(*) as cnt FROM tab";

try{
con = dbcp.getConnection();
pstmt = con.prepareStatement(sql);

rs = pstmt.executeQuery();
if (rs.next()){
System.out.println(rs.getInt("cnt"));
}
}catch(Exception e){
System.out.println(e);
}finally{
dbcp.freeConnection(con, pstmt, rs);
}
}
}

 

 

추가

[펌] : http://godpage.tistory.com/236

 

GenericObjectPool의 설정에 관한 메모이다.
아래의 사이트에서 번역해 온 내용임을 밝힌다.
누군가 벌써 해놨을지도 모르지만..
http://commons.apache.org/pool/apidocs/org/apache/commons/pool/impl/GenericObjectPool.html

설정가능한 ObjectPool의 구현체


적절한 PoolableObjectFactory과 관련하여 GenericObjectPool은
임의의 오브젝트에게 견고한 풀링을 기능적으로 제공한다.

GenericObjectPool은 수많은 설정가능한 파라메터를 제공한다.

* maxActive : 주어진 시간에서의 풀(클라이언트가 보고있거나, 대기상태에 있는)에 의해 할당되어 관리되는오브젝트의 최대수. 설정이 양수가 아닌경우에는 한번에 풀에서 관리될 수 있는 오브젝트의 숫자에는 제한이 없다. 오브젝트의 수가 maxActive에 다다른 경우, 풀은 고갈되었다라고 말한다. 기본 설정은 8이다.

* maxIdle : 어떠한 시간에 풀에서 대기상태로 관리되어 질수 있는 최대 수이다. 음수인경우에는 풀에서 대기상태로 있는 오브젝트의 수에 제한이 없는것으로 간주한다. 기본설정은 8이다.

* whenExhaustedAction : borrowObject() 실행시 풀이 고갈되었을때의 행동을 지정한다.

* WHEN_EXHAUSTED_FAIL, borrowObject() 인경우 NoSuchElementExcption을 던질것이다.

* WHEN_EXHAUSTED_GROW, borrowObject() 인경우 새로운 오브젝트를 만들고 그것을 리턴한다.
(근본적으로 maxActive가 의미가 없어진다. )

* WHEN_EXHAUSTED_BLOCK, borrowObject() 인경우 새 오브젝트혹은 대기오브젝트가 사용가능해 질때까지 블럭- Object.wait()를 호출 - 시킨다. maxWait값이 양수인경우 수 밀리세컨드동안 borrowObject()를 블럭시키고 그후에
NoSuchElementException을 던지게 될것이다. maxWait가 음수라면 borrowObject()메서드는 영원히 블럭된다. (헐~)

* testOnBorrow가 설정되어 있으면, borrowObject()메서드를 통해 오브젝트를 리턴하기 전에 각각의 오브젝트의 유효성을 확인 하려고 할것이다.
(팩토리의 PoolableObjectFactory.validateObject(T) 메서드를 사용한다. )
유효성 체크에 실패하면 풀에서 그 오브젝트를 떨궈내고 다른 오브젝트를 빌려오게 될것이다. 기본설정은 false이다.

* testOnReturn이 설정되어 있으면, returnObject(T)를 통해 풀에 오브젝트를 반환하려고 할때
그 오브젝트의 유효성을 확인 하려고 할것이다. (팩토리의 PoolableObjectFactory.validateObject(T) 메서드를 사용한다.)
유효성 체크에 실패하면 풀어세 떨궈낸다. 기본 설정은 false이다.


옵션으로, 한가지 설정이 더있다. 풀에서 대기상태로 주저 앉아버린 오브젝트들을 쫓아낼 수 있는지 조사하기 위해 그리고 대기 오브젝트 수를 최소값이 되도록 보장하기위한 것이다. 이것은 한개의 'idle object eviction(대기 오부젝트 쫒아내기)' 스레드에
의해 수행되며, 비동기적으로 실행된다. 이 옵션을 설정할 경우는 주의해야한다. Eviction(오브젝트 쫓아내기)은 풀로 부터 오브젝트를 얻으려고하는 클라이언트 스레드와 다툴것이다. 그러므로 Eviction이 매우 빈번하게 일어난다면 성능상의 문제를 가져오게 될것이다. 대기 오브젝트를 쫓아내는 스레드는 아래의 어트리 뷰트들로 설정이 가능할것이다. (할것이다는 머임)

* timeBetweenEvictionRunsMillis : 대기 오브젝트를 쫓아내는 스레드가 "실행" 되기전에 얼마만큼 잠들어 있어야 되는지를 나타낸다. 음수로 되어있으면, 대기 오브젝트 쫓아내는 스레드는 올라오지 않는다. 디폴트 설정은 -1 이다. (즉 기본 설정은 사용안함이다.)

* minEvictableIdleTimeMills : 오브젝트가 풀에서 대기상태로 주저앉기 전에 idleTime에 의거해 쫓아 낼수 있는 최소 시간량을 명시한것이다. (시간제 피씨방, 만화방을 생각하면 되는건가.) 음수로 설정되어 있으면, idle time만으로는 어떤 오브젝트로 쫓아내지 않는다. 이 설정은 timeBetweenEvictionRunsMillis > 0 이 아니면 전혀 효과가 없다. 기본값은 30분이다.

* testWhileIdle : 대기 오브젝트이든 아니든 팩토리의 PoolableObjectFactory.validateObject(T)에 의해
유효성을 체크하게된다. 유효성체크에 실패한 오브젝트는 풀에서 떨궈진다. 이 설정은 timeBetweenEvictionRunsMillis > 0 이 아니면 전혀 효과가 없다.디폴트 설정은 false이다.

* softMinEvictableIdleTimeMills : 오브젝트가 풀에서 대기상태로 주저앉기 전에 대기오브젝트를 쫓아내는 스레드에 의해 쫓겨나게 되는 최소시간량을 몇시한다. 추가적인 조건으로 "minIdle"오브젝트의 인스턴스는 풀에 남아 있어야 된다. 음수로 설정되어 있으면 어떠한 오브젝트가 대기상태에 빠지더라도 쫓겨나지 않는다. timeBetweenEvictionRunsMillis > 0 이 아니면 이 설정은 무효다. 그리고 이 설정은 minEvictableIdleTimeMills가 유효한경우에는 무시된다. 디폴트 설정은 -1이다. (무효)

* numTestsPerEvictionRun : 대기 오브젝트를 쫓아내녀석(스레드)의 갯수. timeBetweenEvictionRunsMillis > 0 이 아니면 이 설정은 무효다. 기본값은 3이다.

대기 오브젝트를 존중해주려면, 풀은 LIFO큐로도 설정이 가능하다. 항상 가장 최근에 사용된 객체가 풀에서 부터 리턴된다. 또는 FIFO 큐는 풀의 가장 오래된 오브젝트 부터 빌려온다.


* lifo : 대기 오브젝트이든 아니든 풀은 last-in-first-out으로 객체를 리턴한다. 디폴트는 true

GenericObjectPoll은 PoolableObjectFactory가 없다면 쓸모없다. null이 아닌 팩토리는 풀이 생성되기 전에 생성자의 인자 혹은 setFactory(org.apache.commons.poll.PoolableObjectFactory)로 제공되어 져야한다.


.
:
Posted by .07274.
2013. 2. 1. 18:28

[펌] Threaded IO streams in Java I.lib()/I.lib(Java)2013. 2. 1. 18:28

.. .. ..

[펌] : http://lifeinide.blogspot.kr/2011/05/threaded-io-streams-in-java.html

Solving some interesting problems in the project I'm currently working on I started to analyse following problem: you have a large data set (eg. a file) on which you need to apply some expensive transformation (eg. compression). The key requirement is a performance.

The best performance demands can usually be effectively fulfilled using multi-threaded algorithms, especially on multiple CPU cores hardware. But you may not to have a multi-threaded transformation algorithm, or you can get a requirement to have many algorithms pluggable, not always designed for parallel work.

I started to think that it's easy to divide source data for chunks and start a thread pool to do the job. This is feasible when you can have multiple output streams, but if you want to write everything to a single file may be a little problematic. If you synchronize all threads to write to a single stream, you rather end up with multi-threaded algorithm working as well as simple single-threaded one. You will simply have no benefits from concurrency, when all threads need to wait for currently writing thread, having a lock. They simply don't do the transformation in the time they wait for the lock. In the result - only one thread works effectively.

If you want to have benefits from concurrency you need to design it in the way, that all threads can do their expensive work independently from the others, without waiting and synchronization.

The conception is following. We have N working threads, and each one is working its way and has been started in different time, in proportion to the others. If we had N buffers with previously established size for each thread, we could assert concurrent writing to these buffers from independent threads without any collision. If the buffer is overfilled, we can flush it to the output stream. If the lock is gained only for flushing, we potentially have other threads working in this time on their buffers, without waiting to have resource available. A Java implementation of this issue, conformed to input/output streams contract in Java is simple (I use apache commons to shorten time required to perform obvious things):

import java.io.BufferedOutputStream;
import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; /** * Wraps target output stream and allows to write to it from multiple threads, handling * underlying data. The data in target output stream is interleaved. It can be then * read then by ThreadedInputStream. * * The stream uses internal buffering and synchronization. The default buffer size is 512KB. * This means, than for each thread there'll be allocated 512KB buffer, when thread starts * to write into the stream. The buffer is flushed into the target output stream periodically. * * Maximum number of threads: Byte.MAX_VALUE * * The lifecycle is following: * <ol> * <li>Create new stream in a main thread * <li>For each worker thread: * <ul> * <li>use the stream in general way * <li>on the end of processing you need to call close(); the stream will not be really closed, * but will be flushed and thread buffers will be removed * </ul> * <li>Call close() in main thread to close stream permanently (as well as the target stream) * </ol> * * @author l0co@wp.pl */ public class ThreadedOutputStream extends OutputStream { protected DataOutputStream target; protected int bufSize = 512*1024; // default buffer size = 512 KB protected volatile byte threadsCount = 0; protected Thread creatorThread; /** Internal thread data holder and buffer **/ protected class ThreadStreamHolder { byte index = 0; int size = 0; byte[] buffer = new byte[bufSize]; public ThreadStreamHolder(byte index) { super(); this.index = index; } /** Flush data to the target stream **/ public void flush() throws IOException { if (size>0) { synchronized (target) { target.writeByte(index); // write thread index target.writeInt(size); // write block size target.write(buffer, 0, size); // write data size = 0; } } } public void write(int b) throws IOException { buffer[size++] = (byte) b; if (size>=bufSize) flush(); } } protected ThreadLocal<ThreadStreamHolder> threads = new ThreadLocal<ThreadedOutputStream.ThreadStreamHolder>(); /** * Creates stream using default buffer size (512 KB). * @param target Target output stream where data will be really written. */ public ThreadedOutputStream(OutputStream target) { super(); this.target = new DataOutputStream(target); creatorThread = Thread.currentThread(); } /** * Creates stream using custom buffer size value. * @param target Target output stream where data will be really written. * @param bufSize Buffer size in bytes. */ public ThreadedOutputStream(OutputStream target, int bufSize) { this(target); this.bufSize = bufSize; } @Override public void write(int b) throws IOException { ThreadStreamHolder sh = threads.get(); if (sh==null) { synchronized (this) { // to avoid more threads with the same threads count if (threadsCount==Byte.MAX_VALUE) throw new IOException("Cannot serve for more than Byte.MAX_VALUE threads"); sh = new ThreadStreamHolder(threadsCount++); // passing threadsCount and ++ is not atomic ! threads.set(sh); } } sh.write(b); } @Override public void flush() throws IOException { super.flush(); // flush the buffers on the end ThreadStreamHolder sh = threads.get(); if (sh!=null) sh.flush(); } @Override public void close() throws IOException { flush(); threads.remove(); // in main thread, close stream if (Thread.currentThread().equals(creatorThread)) target.close(); } public static final int TEST_THREADS = 64; // number of threads public static final double TEST_DPT_MAX = 1024*1024*10; // data amount per thread public static final int TEST_BLOCKSIZE = 1024*512; // default block size public static void main(String[] args) throws IOException { File f = new File("threados"); OutputStream target = new BufferedOutputStream(new FileOutputStream(f, false)); final ThreadedOutputStream out = new ThreadedOutputStream(target, TEST_BLOCKSIZE); ThreadGroup group = new ThreadGroup("threados"); // write some data by threads for (int i=0; i<TEST_THREADS; i++) { final int valueToWrite = (i+5)*20; new Thread(group, new Runnable() { @Override public void run() { try { int jMax = (int) (Math.random()*TEST_DPT_MAX) + 1; byte crc = 0; for (int j=0; j<jMax; j++) { out.write(valueToWrite+j); crc+=(valueToWrite+j); } out.write(crc); System.out.println("Some thread count: "+(jMax+1)); out.close(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } // wait for thread group to finish try { synchronized (group) { if (group.activeCount()>0) group.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } out.close(); } }
The static main() method shows an exemplary usage of the stream from multiple threads. You can manipulate constants to check how does it perform.

If we have such multi-threaded stream and the transformation algorithm performance depends on the input data, the buffers are filled in very random way and we have real benefits from multi-threading. I tested this and it performs very well.

But the output file made this way consists of random blocks of interleaved stream data, written to a single file. So how to handle with restoring data from source file (to apply reverse transformation)? This is why we put into the target stream the thread index and block size value before each block. We can now restore this sequentially, reading each thread source stream separately, or we can do this concurrently, using a thread pool with same threads count as in writing mode. All information can be easily retrieved from interleaved stream:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

/**
 * Allows to read data previously generated by ThreadedOutputStream. This class instance
 * works in single thread and retrieves data from interleaved stream, written by single 
 * thread. The raw source stream is divided on entries, depending on how many threads
 * were writing to the ThreadedOutputStream.
 * 
 * @author l0co@wp.pl
 */
public class ThreadedInputStream extends InputStream {
        
  protected DataInputStream source;
  protected byte entry;
        
  protected int bytesToRead = 0; // how many bytes can we read already from current data block?
        
  protected long pos = 0;
        
  /**
  * Creates the input stream.
  * 
  * @param source Newly instantiated, clean source input stream to raw data
  *      produced by ThreadedOutputStream.
  * @param entry Entry number. There can be many entries in source stream (0, 1, 2... etc,
  *      depending on count of writing threads).
  * @throws EOFException If there's no such entry yet (you need to manually close source then).
  */
  public ThreadedInputStream(InputStream source, byte entry) throws IOException {
    super();

    if (entry>Byte.MAX_VALUE)
      throw new IOException("Cannot serve for more than Byte.MAX_VALUE threads");
                
    this.source = new DataInputStream(source);
    this.entry = entry;
    lookupNextBlock();
  }
        
  protected void lookupNextBlock() throws IOException {
    while (true) {
      byte currentEntry = source.readByte();
                        
      if (currentEntry==entry) {
        // found next entry datablock
        bytesToRead = source.readInt();
        break;
      } else {
        // found next entry, but for different datablock (look for another)
        int blockSize = source.readInt();
        long toSkip = blockSize;
        while (toSkip>0) {
          long skip = source.skip(toSkip);
          if (skip<0)
            throw new EOFException("Cannot skip full datablock");
          toSkip -= skip;
        }                     
      }
    }
  }

  @Override
  public int read() throws IOException {
    if (bytesToRead<=0)
      try {
        lookupNextBlock();
      } catch (EOFException e) {
        return -1;
      }
                
    bytesToRead--;
    return source.read();
  }

  @Override
  public void close() throws IOException {
    source.close();
  }

  // test
  public static void main(String[] args) throws IOException {
    File f = new File("threados");
                
    ThreadGroup group = new ThreadGroup("threados");
                
    // read some data by threads
    Map<Byte, ByteArrayOutputStream> outmap = new LinkedHashMap<Byte, ByteArrayOutputStream>();
                
    try {
      byte i = 0;
      while (true) {
        InputStream source = new BufferedInputStream(new FileInputStream(f));
        try {
          final ThreadedInputStream is = new ThreadedInputStream(source, i++);
          final ByteArrayOutputStream out = new ByteArrayOutputStream();
          outmap.put(i, out);
                                        
          new Thread(group, new Runnable() {
            @Override
            public void run() {
              try {
                IOUtils.copy(is, out);
                is.close();
              } catch (IOException e) {
                e.printStackTrace();
              }
            }
          }).start();
        } catch (EOFException e) {
          source.close();
          break;
        }
      }
    } catch (EOFException e) {} // no more interleaved streams
                
    // wait for threads
    try {
      synchronized (group) {
        if (group.activeCount()>0)
        group.wait();
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
                
    for (byte b: outmap.keySet()) {
      byte[] ba = outmap.get(b).toByteArray();
      System.out.println(b+" ["+ba.length+"]: "+dumpByteArray(ba));
    }
  }
        
  public static String dumpByteArray(byte[] b) {
    StringBuffer sb = new StringBuffer();
    int i = 0;
    byte crc = 0;
    for (byte b1: b) {
      if (i++<20) {
        if (b1<10 && b1>=0)
          sb.append(0);
        sb.append((int) b1 & 0xFF);
        sb.append(',');
      }
                        
      if (i==b.length) {
        if (crc==b1)
          sb.append("crc ok");
        else
          sb.append("crc error");
      } else
          crc+=b1;
    }
    return sb.toString();
  }

}
Also this time main() method should get the file from output stream test, read it in thread pool, and perform simple checksum check.

Depending on requirements, ThreadedInputStream class can be used as a source for multi-threaded or sequential algorithm as well. You can start N threads to get N-stream to fetch data from it, or you can freely iterate through stream data entries in a single thread (in this case you need to perform N iteration).

I'm presenting this solution as a curiosity, but also as a real, working algorithm I used in the project. It works well for solving problems, when you want to parallelize sequential transformation algorithm (in my case it was compression) on a potentially large input data. Of course there's another question how to chunk source data, but this will depend on the source data itself. For unstructured file you can for example divide it to equal parts, and start N reading streams with different starting read index.
.
:
Posted by .07274.
.. .. ..
예외와 오류(error)와 관련된 클래의 상속 계통도는 아래와 같다.


Throwable

-- Error // 심각한 오류 (컴파일시 확인)

-- Exception // 환경상의 잘못으로 인한 예외 (파일 없음 등)

-- RuntimeException // 프로그램상의 오류로 인한 예외


오류(error)는 시스템상의 결함이 발생하여 프로그램이 더 이상 진행하지 못할 정도의 상황이 된 것을 나타낸다. 예를들면 메모리의 부족, 클래스 없음 등이 해당된다.


Exception은 프로그램을 잘못 작성하여 발생하는 예외가 아니라 I/O 문제, 네트웍 다운, 잘못된 이름입력 등 환경이 잘못되어 발생하는 예외를 말하며 이를 확인예외(checked exception) 라고 한다. 이러한 확인예외는 항상 발생할 가능성이 있는 것이므로 이에 대한 처리 방법을 "반드시" 구현해 두어야만 한다. 확인예외의 처리방법은 프로그래머가 항상 구체적으로 기술해야 하는데 이러한 확인예외 처리방법에는 try/catch 를 이용하여 구체적으로 기술하는 방법과, 상위 메소드에게 예외발생을 알리는(throws 하는) 방법 크게 두 가지로 나눌 수 있다.


RuntimeException은 프로그램을 잘못 작성하여 발생하는 예외로서 어레이의 경계를 넘은 경우, 조건문의 처리 잘못 등이 해당된다. 이것은 프로그램을 오류없이 작성하였다면 발생하지 않게 되는 예외이므로 이러한 예외의 처리를 자바가 반드시 요구하지는 않는다. 그러나 ArithmeticException 과 같은 RuntimeException 예외는 처리해 두면 편리하다.

 

.
:
Posted by .07274.
.. .. ..

[펌] : http://agbird.egloos.com/4863601

 

 

ConcurrentHashMap vs. HashTable 이란 글에서 아래와 같은 코드는 잘못된 방법일 뿐더러 위험한 방법이라고 했습니다.

class SharedData {
private Integer intData = 0;
private Boolean boolData = false;

public int getInt() { synchronized (intData) { return intData; } }
public void setInt(int n) { synchronized (intData) { intData = n; } }
public boolean getBool() { synchronized (boolData) { return boolData; } }
public void setBool(boolean b) { synchronized (boolData) { boolData = b; } }
}

우선 잘못된 이유에 대해서 먼저 설명하자면 프로그래머의 의도와 달리 intData 나 boolData 객체는 동기화되지 않습니다. 그 이유는 setInt() 나 setBool() 함수가 호출될 때마다 락으로 사용되는 intData 나 boolData 객체가 변할 수 있는데 이런 경우 쓰레드들이 서로 다른 락에 접근하기 때문입니다. 예를 들어 다음과 같은 코드가 있다고 합시다.

public class SyncTest {
static private Object lock = new Object();

static class TestRunnable implements Runnable {
@Override
public void run() {
try {
synchronized (lock) {
System.out.println("before sleep in thread");
Thread.sleep(1000);
System.out.println("after sleep in thread");
}
} catch (Interrupted Exception e) {
}
}
}

public static void main(String[] args) {
ExecutorService threads = Executors.newFixedThreadPool(2);
threads.submit(new TestRunnable());
threads.submit(new TestRunnable());
threads.shutdown();
try {
threads.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
}
System.exit(0);
}
}

위 코드를 실행시켜 보면 항상 아래처럼 출력됩니다.

before sleep in thread
after sleep in thread
before sleep in thread
after sleep in thread

그러나 위에 동기화 부분을 다음과 같이 고치면 문제가 발생합니다.

synchronized (lock) {
lock = new Object(); // assigned another object to lock
System.out.println("before sleep in thread");
Thread.sleep(1000);
System.out.println("after sleep in thread");
}

lock 객체가 동기화 블럭내에서 다른 객체로 변경되었기 때문에 이후에 다른 쓰레드에서 lock 객체에 접근하면 더이상 쓰레드간 동기화 효과가 없습니다. 자바에서 모든 객체는 레퍼런스 객체입니다. 그런데 synchronized 블럭에서 동기화를 위해 참조하는 객체는 이 레퍼런스 객체가 아니라 레퍼런스 객체가 참조하고 있는 실제 객체입니다. 따라서 비록 같은 lock 객체라 하더라도 이 객체가 참조하는 실제 객체가 바뀌었기 때문에 쓰레드간 동기화는 깨지고 맙니다.

이 글의 처음에 소개된 SharedData 클래스의 잘못된 점은 intData 나 boolData 가 setter 메소드에서 다른 값이 할당되는 순간 Auto boxing 에 의해 다른 객체를 참조하기 때문입니다. 그러므로 이 시점에 다른 쓰레드가 synchronized 블럭에 접근하게 되더라도 다른 객체에 접근하기 때문에 락이 해제될때까지 기다리지 않습니다. 따라서 어떤 경우라도 synchronized 블럭 내에서 락 객체의 참조값을 변경하지 말아야 합니다. 보통 이런 실수를 할 소지는 적습니다만 SharedData 클래스 예처럼 Auto boxing 이 발생하는 Integer, Boolean, Long 등과 같은 클래스를 사용할 때는 자칫 착각할 수 있습니다.

이제 SharedData 클래스의 동기화 방식이 위험한 이유에 대해서 설명하겠습니다. 우선 아래 코드를 보시기 바랍니다.

public class SyncTest {
static private String lock = "This is a lock;

static class TestRunnable implements Runnable {
@Override
public void run() {
try {
synchronized (lock) {
System.out.println("before sleep in thread");
Thread.sleep(1000);
System.out.println("after sleep in thread");
}
} catch (Interrupted Exception e) {
}
}
}

public static void main(String[] args) {
// 이전 코드와 동일...
}
}

위 코드가 제대로 동작할까요?
- 예 그렇습니다.
그렇다면 좋은 방법일까요?
- 아뇨 그렇지 않습니다(Joshua Bloch 말투 좀 흉내내 봤습니다).

그러면 왜 좋은 방법이 아닐까요? 왜냐하면 String 객체를 락으로 사용하는 것은 예기치 않은 dead lock 을 발생시키기 때문입니다. 예를 들어 아래 코드를 보시죠.

public class BadLockSample {
static void main(String[] args) throws Exception {
String lock = "This is a lock";
synchronized (lock) {
Future<String> future = Executors.newSingleThreadExecutor().submit(new Callable<String>() {
@Override
public String call() throws Exception {
String anotherLock = "This is a lock";
synchronized (anotherLock) {
return "Result";
}
}
});
System.out.println(future.get());
}
}
}

위 코드에서 메인 쓰레드와 Callable 클래스가 호출되는 쓰레드는 서로 다른 락 객체를 사용하고 있습니다만 실행시켜보면 데드락이 발생하여 프로그램이 종료하지 않습니다. 왜 그럴까요? 그 이유는 자바에서 문자열을 다른 객체와 달리 특별한 방식으로 관리 하기 때문입니다. 자바는 메모리를 절약하기 위해 컴파일 시점에 평가 가능한 문자열에 대해서 영구 메모리에 문자열을 저장하며 이 문자열을 참조하는 String 객체들은 명시적으로 new String() 을 사용해서 객체를 생성하지 않는한 같은 문자열일 경우 동일한 객체를 참조하게 됩니다(혹은 String.intern() 메소드를 호출하면 명시적으로 이런 처리가 가능합니다).
결국 위에서 lock 과 anotherLock 은 다른 레퍼런스 객체이지만 동일한 상수 문자열을 참조하기 때문에 사실 동일한 객체나 마찬가지입니다. 따라서 위 프로그램의 메인 쓰레드와 Callable 쓰레드는 같은 락을 획득하려고 경쟁하기 때문에 데드락이 발생합니다.
그런데 이게 SharedData 클래스와 무슨 관계일까요? SharedData 에서는 String 이 아니라 Integer 와 Boolean 객체를 사용했고 이들 클래스는 Auto Boxing 에 의해 primitive type 값을 자동으로 해당 타입에 맞는 객체로 생성해줍니다. 따라서 String 에서처럼 같은 객체를 참조할 일은 없을 듯 싶습니다.
하지만 실제로는 그렇지 않습니다. 왜냐하면 Integer 나 Boolean 같은 Wrapping type class 들은 성능 향상을 위해 몇몇 값들에 대해서는 매번 객체를 새로 생성하는 것이 아니라 미리 만들어 놓은 객체를 재 사용하기 때문입니다(이런 방식을 Flyweight pattern 이라고 합니다).
따라서 SharedData 객체를 만약 아래와 같이 사용하게 되면 데드락이 발생합니다.

public static void main(String[] args) {
static void main(String[] args) throws Exception {
Integer lock= 0;
synchronized (lock) {
Future<String> future = Executors.newSingleThreadExecutor().submit(new Callable<String>() {
@Override
public String call() throws Exception {
return new SharedData().getInt();
}
});
System.out.println(future.get());
}
}
}

결론적으로 값 객체를 직접 락으로 사용하지 말아야 합니다. 꼭 값 객체를 별도의 락으로 동기화시키려면 java.util.concurrent.lock.ReentrantLock 같은 락 전용 클래스 객체를 사용하는 것이 좋습니다.

p.s. 노파심에서 언급하는 건데...혹여라도 ReentrantLock 을 아래처럼 사용하지는 마세요...

Lock lock = new ReentrantLock();
synchronized (lock) {
....
}

java.util.concurrent.lock.Lock 관련 클래스들의 올바른 사용법은 다음과 같습니다.

Lock lock = new ReentrantLock();
lock.lock();
try {
....
} finally {
lock.unlock();
}

 

.
:
Posted by .07274.
2013. 1. 21. 15:24

[펌] Java File Writing 성능 비교 I.lib()/I.lib(Java)2013. 1. 21. 15:24

.. .. ..

[펌] : http://bcho.tistory.com/288

 

JAPM을 업그레이드 할까 싶어서 Log Writing 부분을 개선하고자 해서

File Writing을 어떻게 하는 것이 제일 빠를까 테스트를 해봤다.
크게 아래 케이스인데.

1. FileWriter fw = new FileWriter(LOG_HOME+"writer.log");
2. BufferedWriter bw = new BufferedWriter(new FileWriter(LOG_HOME+"writer.log"));
3. FileOutputStream fos = new FileOutputStream(LOG_HOME+"outputstream.log");
4. BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(LOG_HOME+"bufferedoutputstream.log"));
5. FileChannel fc =(new FileOutputStream(new File(LOG_HOME+"filechannel.log"))).getChannel(); + Byte Buffer 매번 생성
6. FileChannel fc =(new FileOutputStream(new File(LOG_HOME+"filechannel.log"))).getChannel(); + ByteBuffer 재사용

테스트의 정확성을 위해서 측정중에 GC가 발생하지 않도록 NewSize와 HeapSize를 크게 해놓고 테스트를 하였다. Vm 옵션은 -XX:NewSize=480m -ms768m -mx768m -verbosegc 이다.
환경 : Windows XP, Sun JVM 1.5, Centrio VPro Dual core, IBM Thinkpad X61s

결과는 다음과 같다.
1K 2K 5K 10K 50K
FileWriter 31 32 94 203 1281
FileWriter + BufferedWriter 15 31 94 188 1000
FileOutputStream 32 47 109 188 1063
FileOutputStream + BufferedOutputStream 31 47 109 203 1578
FileChannel 47 63 109 219 2906
FileChannel + Byte Buffer 재사용 31 47 188 250 2766

(해당 레코드를 1000번씩 write)

예상하기로는 NIO의 FileChannel이 가장 빠를것이라 생각했는데, 의외로 FileWrite와 FileOutputStream의 성능이 높게 나왔다. NIO의 경우 JVM 벤더에 종속성이 있겠지만 이런 결과가 나오는것은 약간 의외였다.
오히려 프로그래밍 기법을 내서 FileChannel을 사용할 경우 최대 3배까지 성능이 나쁜 경우가 올 수 있으니. 이런건 차라리 모르는게 나을 수 도 있겠다~~ 싶다.. ^^

BufferedWriter등의 경우 GC를 유발하는 문제가 있을 수 있기 때문에 또 다른 성능 저하 요인이 될 수 는 있겠지만, 현재까지 테스트 결과로는 Windows Platform에서는 FileWriter + BufferedWriter의 경우가 성능이 제일 좋은것 같다.
아래는 테스트에 사용한 소스 코드
==
package bcho.filewriter.test;

import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

import junit.framework.TestCase;

public class FileWriterTest extends TestCase {
final static int loop = 1000;
final static String LOG_HOME="c:/temp/";
static String LOG_STRING="";
static {
}
public void testSuite() throws Exception{
int stringSize[]={100,200,500,1000,5000};
for(int i=0;i<stringSize.length;i++){
LOG_STRING="";
for(int j=0;j<stringSize[i];j++) LOG_STRING+="1234567890";
log(stringSize[i]+"0 bytes");
testFileWriter();
System.gc();
testBufferedWriter();
System.gc();
testFileOutputStream();
System.gc();
testFileBufferedOutputStream();
System.gc();
testFileChannel();
System.gc();
testFileChannelOneBuffer();
System.gc();
}
}
public static void log(String str){
System.out.println(str);
}
// java.io.FileWriter
private void testFileWriter() throws Exception {
FileWriter fw = new FileWriter(LOG_HOME+"writer.log");
long st = Timer.getCurrentTime();
for(int i =0;i<loop;i++){
fw.write(LOG_STRING);
}
log("FileWriter :"+Timer.getElapsedTime(st) +"ms");
fw.close();
}
// java.io.BufferedWriter
private void testBufferedWriter() throws Exception {
BufferedWriter bw = new BufferedWriter(new FileWriter(LOG_HOME+"writer.log"));
long st = Timer.getCurrentTime();
for(int i =0;i<loop;i++){
bw.write(LOG_STRING);
}
log("BufferedWriter :"+Timer.getElapsedTime(st) +"ms");
bw.close();
}
// java.io.FileOutputStream
private void testFileOutputStream() throws Exception{
FileOutputStream fos = new FileOutputStream(LOG_HOME+"outputstream.log");
long st = Timer.getCurrentTime();
for(int i=0;i<loop;i++){
byte[] buf = LOG_STRING.getBytes();
fos.write(buf);
}
log("FileOutputStream :"+Timer.getElapsedTime(st) +"ms");
fos.close();
}
// java.io.FileOutputStream
// + java.io.BufferedOutputStream
private void testFileBufferedOutputStream() throws Exception{
BufferedOutputStream fos =
new BufferedOutputStream(
new FileOutputStream(LOG_HOME+"bufferedoutputstream.log"));
long st = Timer.getCurrentTime();
for(int i=0;i<loop;i++){
byte[] buf = LOG_STRING.getBytes();
fos.write(buf);
}
log("FileBufferedOutputStream :"+Timer.getElapsedTime(st) +"ms");
fos.close();
}
private void testFileChannel() throws Exception {
FileChannel fc =(new FileOutputStream(new File(LOG_HOME+"filechannel.log"))).getChannel();
long st = Timer.getCurrentTime();
for(int i=0;i<loop;i++){
byte[] buf = LOG_STRING.getBytes();
ByteBuffer bytebuffer = ByteBuffer.allocate(buf.length);
bytebuffer.put(buf);
bytebuffer.flip();
fc.write(bytebuffer);
}
log("FileChannel :"+Timer.getElapsedTime(st) +"ms");
fc.close();
}
private void testFileChannelOneBuffer() throws Exception {
FileChannel fc =(new FileOutputStream(new File(LOG_HOME+"filechannelonebuf.log"))).getChannel();
int BUF_SIZE=1000;
long st = Timer.getCurrentTime();
ByteBuffer bytebuffer = ByteBuffer.allocate(BUF_SIZE);
for(int i=0;i<loop;i++){
byte[] buf = LOG_STRING.getBytes();
int offset=0;
int length= buf.length;
while(offset < length){
int chunkSize = BUF_SIZE > length-offset ? length-offset-1 : BUF_SIZE;
bytebuffer.put(buf, offset, chunkSize);
bytebuffer.flip();
offset+=BUF_SIZE;
fc.write(bytebuffer);
bytebuffer.clear();

}
}
log("FileChannel with reusing buffer:"+Timer.getElapsedTime(st) +"ms");
fc.close();
}


}

 

.
:
Posted by .07274.
2013. 1. 15. 17:41

BlockingQueue I.lib()/I.lib(Java)2013. 1. 15. 17:41

.. .. ..

3중 1번 클레스

package concurent;

import java.util.concurrent.*;

public class blockingQuere11 {
public static void main(String[] args) {
BlockingQueue<String> drop = new ArrayBlockingQueue(1, true);
(new Thread(new blockingQuere13(drop))).start();
(new Thread(new blockingQuere12(drop))).start();
}
}

3중 2번 클레스

package concurent;

import java.util.*;
import java.util.concurrent.*;

class blockingQuere12 implements Runnable{

private BlockingQueue<String> drop;

public blockingQuere12(BlockingQueue<String> d) {
this.drop = d; }

public void run(){
try{
String msg = null;

while (!((msg = drop.take()).equals("DONE")))
System.out.println("out : "+ System.currentTimeMillis() + msg);

}catch (InterruptedException intEx){
System.out.println("Interrupted! " + "Last one out, turn out the lights!");
}
}
}

3중 3클래스

package concurent;

import java.util.*;
import java.util.concurrent.*;

public class blockingQuere13 implements Runnable {

private BlockingQueue<String> drop;
List<String> messages = Arrays.asList("1111", "2222",
"3333", "44444","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25");

public blockingQuere13(BlockingQueue<String> d) {
this.drop = d;
}

public void run() {
try {
for (String s : messages){
drop.put(s);
System.out.println("i n : "+ System.currentTimeMillis() + s);
}
drop.put("DONE");
} catch (InterruptedException intEx) {
System.out.println("Interrupted! "+ "Last one out, turn out the lights!");
}
}
}

결과값

out : 13582386103091111
i n : 13582386103091111
out : 13582386103092222
i n : 13582386103092222
out : 13582386103093333
i n : 13582386103093333
out : 135823861030944444
i n : 135823861030944444
out : 13582386103095
i n : 13582386103095
out : 13582386103096
i n : 13582386103096
out : 13582386103107
i n : 13582386103107
out : 13582386103108
i n : 13582386103108
out : 13582386103109
i n : 13582386103109
out : 135823861031010
i n : 135823861031010
out : 135823861031011
i n : 135823861031011
out : 135823861031012
i n : 135823861031012
out : 135823861031013
i n : 135823861031013
out : 135823861031014
i n : 135823861031014
out : 135823861031015
i n : 135823861031015
out : 135823861031016
i n : 135823861031016
out : 135823861031017
i n : 135823861031017
out : 135823861031018
i n : 135823861031118
out : 135823861031119
i n : 135823861031119
out : 135823861031120
i n : 135823861031120
out : 135823861031121
i n : 135823861031121
out : 135823861031122
i n : 135823861031122
out : 135823861031123
i n : 135823861031123
out : 135823861031124
i n : 135823861031124
out : 135823861031125
i n : 135823861031125


.
:
Posted by .07274.