IT_Programming/Java

Java 에서의 Object Pool 기법

JJun ™ 2011. 7. 4. 10:44

-----------------------------------------------------------------------------------------------

출처: 이원영 님의 글

-----------------------------------------------------------------------------------------------

 

Object Pool Control 기법은 JDBC Connection Pool 을 만들거나, TCP/IP Socket Pool을 만드는 등
최근엔 많은 자바 개발자들이 즐겨 사용하는 기법 중의 하나 입니다.


특히 JDBC Connection Pool 이 소개 되면서, 너도 나도 직접 Connection Pool을 만드는 시도를

자연스럽게 하게 됩니다.

 

저 역시 그 중의 하나였고, Object Pooling 기법에 대해 너무 단순하고 쉽게 생각 했었습니다.
최근에야 저의 프로그래밍 기법에 엄청난 잘못이 있었다는 것을 하나 은행과 한빛 은행 인터넷 뱅킹

프로젝트 튜닝작업을 하면서 깨달았습니다.


저와 같은 잘못을, 행여나 다른 분들이 똑같이 범하지 않았으면 하는 바램에서 저의 잘못을 아래 처럼

기술합니다.


아래의 3 개의 클래스는 Object Pool 을 테스트 하는 프로그램입니다. 뭐가 잘못되었을까요?

퀴즈입니다.

 

 

 


 


package badpool;
/*
 * Lee WonYoung
 *
javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */

public class BadPoolTest extends Thread{
    private int client = 0;
    public BadPoolTest(int n){
        this.client = n;
    }
    public static void main(java.lang.String[] args) {
        // PoolObject 의 최대값인 20 보다 많은 요청을 보내어야 테스트가 됨.
        int max = 40;
        System.out.println("Calling " + max + " threads");

        Thread[] threads = new BadPoolTest[max];
        for(int i=0;i<max;i++){
            threads[i] = new BadPoolTest(i);
            threads[i].start();
        }
    }
    public void run(){
        PoolManager manager = null;
        PoolObject obj = null;
        try {
            manager = PoolManager.getInstance();
            obj = manager.getPoolObject();
            obj.execute();
        }
        finally{
            if ( obj != null ) manager.release(obj);
        }
    }
}

 


 

 

 

package badpool;
/*
 * Lee WonYoung
 *
javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */

public class PoolManager {
    private static PoolManager manager = null;

    private int MAX = 20;
    private int count = 0;
    private java.util.Vector pool = new java.util.Vector();

    private PoolManager() {
        super();
        for(int i=0;i<MAX;i++){
            pool.addElement(new PoolObject(new Integer(i)));
        }
    }
    public static synchronized PoolManager getInstance() {
        if ( manager == null ) {
            manager = new PoolManager();
        }
        return manager;   
    }
    public synchronized PoolObject getPoolObject() {
        PoolObject obj = null;
        while( count >= MAX ) {
            try{
                Thread.sleep(1000);
            }catch(Exception e){}
            System.out.println("PoolManager : sleep... current count=" + count);
        }
        obj = (PoolObject)pool.elementAt(0);
        pool.removeElementAt(0);
        count++;
        return obj;
    }
    public synchronized void release(PoolObject obj) {
        count--;
        pool.addElement(obj);
        System.out.println("PoolManager: released ... current count=" + count);
    }
}

 

 


 

 


package badpool;

/*
 * Lee WonYoung
 *
javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */
public class PoolObject {
        private static java.util.Random random = new java.util.Random();
        private Integer index = null;
   
    public PoolObject(Integer i) {
        this.index = i;
    }
    public void execute() {
        // 어떤 비즈니스 로직....
        System.out.println("Thread-" + index + ": started.");
        int second = 0;
        try{
            // 5 - 15 seconds random sleep.
            second = 5 + (int)(random.nextDouble()*10);
            Thread.sleep(second*1000);
        }catch(Exception e){}
        System.out.println("Thread-" + index + ": elapsed=" + second);
    }
}


 


 

 

위 클래스들을 컴파일 하여 돌리면, 40개의 Thread 가 모두 동작하는 것이 아니라 20개만 수행하고

dead-lock 에 빠집니다. 왜냐면, 한정된 20 개의 PoolObject 을 동시에 40개의 Client Thread 가

요청하게 되면, 그 중 20개는 getPoolObject()을 성공하게 되어 정상적인 수행을 진행하게 되고,

나머지 20개는 synchronized lock 으로 인해 대기하게 됩니다.

 

한참이 지난 후 수행중이었던 20개의 Thread 가 사용하고 난 PoolObject 을 PoolManager 에게

반환(release)하려 합니다.


문제는 여기서 발생하죠. 같은 lock 객체에 의해 synchronized 가 걸려 있으니, 앞에서 getPoolObject() 을

미처 수행하지 못하고 대기하던 20개의 Thread 가 해당 lock 을 미리 점유하고 있어서, release() 메소드

내의 synchronized(lock) 블록으로 들어가지를 못하게 되는 겁니다.


" 다 사용했으나 반환을 못하고, 받지를 못했으니 나머지 20개의 Thread 는 마냥 기다리고만 있습니다. "

 

물론 getPoolObject() 내에서 대부분 2-3 회 반복적으로 시도하다가 가용한 자원이 끝까지 생기지 않으면

튕켜 나가게 로직을 짜곤 합니다. 이 상황이라면 dead-lock 까지는 가지 않게 되겠지만, "끼적끼적" 겨우

하나씩 동작하게 되지요.

 

외견상으로는 "왜 이렇게 시스템이 갑자기 느려졌지?" 라는 형태로 나타나게 됩니다.
혹시 자신이 만든 Connection Pool 에서 Pool 최대값을 넘지 않을 때는 잘 동작하는데,

동시에 최대값 이상의 요청이 급작스럽게 들어오고 난 이 후부터 시스템이 급격하게 느려지거나 hang 이

발생한 적이 없었던가요?

 

아래는 고민하고 고민하여 나름대로 최적화된 모습이라 생각되는 방식을 찾아 보았습니다.

보다 더 나은 알고리즘이 있으면 조언 부탁합니다.

 

프로그램 설명은 각자 개인 스스로의 몫으로 돌렸으면 합니다.
주석을 이래저래 풀어 가며 테스트를 해 보시면 어떤 서로다른 결과가 나타나는지 직접 목격할 수

있을 겁니다.


 


 

 

 

package pool;

/*
 * Lee WonYoung
 *
javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */
public class PoolTest extends Thread{
    private int client = 0;
    public PoolTest(int n){
        this.client = n;
    }
    public static void main(java.lang.String[] args) {
        // PoolObject 의 최대값인 20 보다 많은 요청을 보내어야 테스트가 됨.
        int max = 40;
        System.out.println("Calling " + max + " threads");

        Thread[] threads = new PoolTest[max];
        for(int i=0;i<max;i++){
            threads[i] = new PoolTest(i);
            threads[i].start();
        }
    }
    public void run(){
        PoolManager manager = null;
        PoolObject obj = null;
        try {
            manager = PoolManager.getInstance();
            obj = manager.getPoolObject();
            obj.execute();
        }
        finally{
            if ( obj != null ) manager.release(obj);
        }
    }
}

 

 


 

 

 

package pool;

/*
 * Lee WonYoung
 *
javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */
public class PoolManager {
    private static PoolManager manager = null;

    private int MAX = 20;
    private int count = 0;
    private java.util.Vector pool = new java.util.Vector();

    private PoolManager() {
        for(int i=0;i<MAX;i++){
            pool.addElement( new PoolObject(new Integer(i)) );
        }
    }
    public static synchronized PoolManager getInstance() {
        if ( manager == null ) {
            manager = new PoolManager();
        }
        return manager;   
    }
    /**
     * PS: getPoolObject() 에서 파라메터로 "int from" 을 실제로는 받을 필요가
     *     없으나 어떤 Client Thread 에서 요청이 온지를 디버깅 하기 위해 넣어둔 것
     *     뿐임.
     */

    
    public synchronized PoolObject getPoolObject(int from) {
        while( count >= MAX ) {
            try{
                System.out.println("getPoolObject(from:" + from +
                                ") : sleep... active count=" + count);
               
                // Max값을 초과한 나머지는 모두 아래 wait(timeout) 에서 대기함.
                // 아래에서 1초를 wait 하게 하지만, 실제로는 그 이전에
                // notify() 에 의해 깨어나게 됨.
                wait(1*1000);
                //System.out.println("awaiked. ...");

            }catch(Exception e){
                System.out.println("getPoolObject: awaiked " + e.toString());
            }
        }
        PoolObject obj = (PoolObject)pool.elementAt(0);
        pool.removeElementAt(0);
        count++;
        System.out.println("getPoolObject(from:" + from +"): return : active count=" + count);
        return obj;
    }
    public synchronized void release(PoolObject obj) {
        count--;
        pool.addElement(obj);

        //notify();
        notifyAll();

        // 만약 notifyAll() 을 하면 getPoolObject()에서 wait()에서 걸린
        // 모든 Thread 가 일시에 일어나게 되어 쓸데없이 전부 while 문을 동시에
        // 확인하게되고, 이는 CPU 부하를 야기할 것으로 생각되나,
        // 일부 책에서는 notify() 대신 notifyAll() 을 권장하기도 함.
        // 두가지를 모두 테스트해 보면 아주 재밌는 현상을 만날 수 있을 것임.

        System.out.println("Thread-" + obj.getIndex() + ": released :active count=" + count);
    }
}

 

 


 

 

 

package pool;

/*
 * Lee WonYoung
 *
javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */
public class PoolObject {
        private static java.util.Random random = new java.util.Random();
        private Integer index = null;
   
    public PoolObject(Integer i) {
        this.index = i;
    }
    public void execute() {
        System.out.println("Thread-" + index + ": started.");
        int second = 0;
        try{
            // 5 - 15 seconds random sleep.
            second = 5 + (int)(random.nextDouble()*10);
            Thread.sleep(second*1000);
        }catch(Exception e){}
        System.out.println("Thread-" + index + ": elapsed=" + second);
    }
    public Integer getIndex() {
        return index;
    }
}

 

 


 

 


핵심은 어떤 경우든, Thread.sleep() 을 이용하여 가용한 자원이 생길때까지 기다리게 해서는 안된다는

사실입니다. wait() 와 notify(),notifyAll() 을 이용하여만 합니다.

 

NOTE: wait() 은 synchronized block 의 lock 을 해제합니다. 따라서 연이어 들오는 쓰레드들이

          sync block 안으로 들어와 wait() 부분에서 함께 waiting 하게 됩니다.

          그러나, 위의 샘플은 실 프로젝트 시스템에서는 사용할 법한 소스가 아닙니다.

          중요한 한 가지 요소가 빠져 있습니다. 갯수에 제한이 있는 자원에 대한 요청이 엄청나게
          폭주할 경우, 가용한 자원을 할당 받지 못한 요청들은 위 로직대로라면 상당히 오랫동안 
          기다리게 됩니다. 이는 시스템의 hang 현상을 유발시킬 수 있는 절대적인 잠재력을 안고 있습니다.

          일정한 TIME_WAIT 을 두어 일정시간을 기대려도 가용한 자원이 여전히 생기지 않을 경우 Fail 이

          일어나게 하여 튕켜 나가게 하여야만 hang 으로 이어지지 않습니다.

 

 

아래는 실 프로젝트에서 사용할 법한 로직입니다.


package org.jsn.pool;

/*
 * Lee WonYoung
 *
javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */
public class PoolManager {
    private static PoolManager manager = null;

    private int MAX_RESOURCE = 20;
    private long TIME_WAIT = 2 * 1000;
    private java.util.Hashtable pool = new java.util.Hashtable();
    private java.util.Stack freeStack = new java.util.Stack();

    private PoolManager() {
        for(int i=0;i<MAX_RESOURCE;i++){
            Integer index = new Integer(i);
            pool.put( index,  new PoolObject(index) );
            freeStack.push(index);
        }
    }
    public static synchronized PoolManager getInstance() {
        if ( manager == null ) {
            manager = new PoolManager();
        }
        return manager;   
    }
    public  PoolObject getPoolObject() throws Exception {
        Integer index = null;
        long start = System.currentTimeMillis();
        synchronized(this){
            while( freeStack.empty()  ) {
                try{
                    // 나머지는 모두 아래 wait(timeout) 에서 대기함.
                    // 아래에서 1 초를 wait 하게 하지만, 실제로는 그 이전에
                    // notify() 에 의해 깨어나게 됨.
                    wait(1 * 1000);
                   
                }catch(Exception e){
                    System.err.println("getPoolObject: awaiked " + e.toString());
                }
                long end = System.currentTimeMillis();
                if ( freeStack.empty() && (end - start) >= TIME_WAIT ) {
                    throw new Exception("getPoolObject : timeout(" + TIME_WAIT + ") exceed");
                }
            }
            index = (Integer)freeStack.pop();
        }
        PoolObject obj = (PoolObject)pool.get(index);
        return obj;
    }
    public void release(PoolObject obj) {
        synchronized ( this ){
            freeStack.push(obj.getIndex());
           
            //notify();
            notifyAll();
            // 만약 notifyAll() 을 하면 getPoolObject()에서 wait()에서 걸린
            // 모든 Thread 가 일시에 일어나게 되어 쓸데없이 전부 while 문을 동시에
            // 확인하게되고, 이는 CPU 부하를 야기할 것으로 생각되나,
            // 일부 책에서는 notify() 대신 notifyAll() 을 권장하기도 함.
            // 두가지를 모두 테스트해 보면 아주 재밌는 현상을 만날 수 있을 것임.
        }
        //System.out.println("Object-" + obj.getIndex() + ": released.");
    }
}

 


 

 

 

필요할 경우, 위의 java.util.Stack 대신 Queue 를 사용하실 수 있습니다.
사용하는 자원만 지속적으로 사용하고, Stack 의 저 아래에 있는 특정 자원은 한번도 사용되지 않을 것

같은 불안감(?)이 드신다면 Stack 대신 Queue 를 이용하여 다음처럼 골고루(?) 사용토록 고쳐셔도 됩니다.

 

 

package org.jsn.pool;

/*
 * Lee WonYoung
 *
javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */

public class PoolManager {
    private static PoolManager manager = null;

    private int MAX_RESOURCE = 20;
    private long TIME_WAIT = 2 * 1000;
    private java.util.Hashtable pool = new java.util.Hashtable();
    private org.jdf.util.Queue freeQueue = new org.jdf.util.Queue();

    private PoolManager() {
        for(int i=0;i<MAX_RESOURCE;i++){
            Integer index = new Integer(i);
            pool.put( index,  new PoolObject(index) );
            freeQueue.enqueue(index);
        }
    }
    public static synchronized PoolManager getInstance() {
        if ( manager == null ) {
            manager = new PoolManager();
        }
        return manager;   
    }
    public  PoolObject getPoolObject() throws Exception {
        Integer index = null;
        long start = System.currentTimeMillis();
        synchronized(this){
            while( freeQueue.empty() ) {
                try{
                    // 나머지는 모두 아래 wait(timeout) 에서 대기함.
                    // 아래에서 1 초를 wait 하게 하지만, 실제로는 그 이전에
                    // notify() 에 의해 깨어나게 됨.
                    wait(1 * 1000);

                }catch(Exception e){
                    System.err.println("getPoolObject: awaiked " + e.toString());
                }
                long end = System.currentTimeMillis();
                if ( freeQueue.empty() && (end - start) >= TIME_WAIT ) {
                    throw new Exception("getPoolObject : timeout(" + TIME_WAIT + ") exceed");
                }
            }
            index = (Integer)freeQueue.dequeue();
        }
        PoolObject obj = (PoolObject)pool.get(index);
        return obj;
    }
    public void release(PoolObject obj) {
        synchronized ( this ){
            freeQueue.enqueue(obj.getIndex());
           
            //notify();
            notifyAll();
            // 만약 notifyAll() 을 하면 getPoolObject()에서 wait()에서 걸린
            // 모든 Thread 가 일시에 일어나게 되어 쓸데없이 전부 while 문을 동시에
            // 확인하게되고, 이는 CPU 부하를 야기할 것으로 생각되나,
            // 일부 책에서는 notify() 대신 notifyAll() 을 권장하기도 함.
            // 두가지를 모두 테스트해 보면 아주 재밌는 현상을 만날 수 있을 것임.
        }
       //System.out.println("Object-" + obj.getIndex() + ": released.");
    }
}

 


 


Queue.java
http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=981829799

 

Re: LinkedList 를 이용한 Queue.java
http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=987580631

 

더보기

제목 : Queue.java
글쓴이: 이원영(javaservice) 2001/02/11 03:29:59 조회수:6130 줄수:140
--------------------------------------------------------------------------------
/*
 * @(#)Queue.java   1.0 2001/02/11
 *
 * (c) Copyright JavaService.Net, 2001. All rights reserved.
 *
 * NOTICE !      You can copy or redistribute this code freely, 
 * but you should not remove the information about the copyright notice 
 * and the author.
 * 
 * @author  Lee WonYoung  javaservice@hanmail.net, lwy@kr.ibm.com
 * 
 */
package org.jdf.util;
/**
 *
 * The <code>Queue</code> class represents a first-in-first-out 
 * (FIFO) queue of objects. 
 *
 * @author  Lee WonYoung  javaservice@hanmail.net, lwy@kr.ibm.com
 * @version 1.0 2001/02/11
 *
 */
public class Queue extends java.util.Vector
{
    public Queue(){
        super();
    }
    public Queue(int initialCapacity){
        super(initialCapacity);
    }
    public Queue(int initialCapacity, int capacityIncrement){
        super(initialCapacity,capacityIncrement);
    }
    /**
     * Tests if this queue is empty.
     *
     * @return  <code>true</code> if this queue is empty;
     *          <code>false</code> otherwise.
     */
    public boolean empty(){
        return size() == 0;
    }
    /**
     * Add an item to the end of the queue. 
     *
     * @param   item   the item to be pushed to the end of this queue.
     * @return  the <code>item</code> argument.
     */
    public synchronized Object enqueue(Object item){
        addEmement(item);
        return item;
    }
    /**
     * Removes the object at the front of this queue and returns
     * that object as the value of this function. 
     *
     * @return     The object at the front of this queue.
     * @exception  EmptyQueueException  if this queue is empty.
     */
     public synchronized Object dequeue(){
        Object obj = peek();
        removeElementAt(0);
        return obj;
    }
    /**
     * Looks at the object at the front of this queue without removing it 
     * from the queue. 
     *
     * @return     the object at the top of this queue. 
     * @exception  EmptyQueueException  if this queue is empty.
     */
    public synchronized Object peek() {
        int len = size();
        if (len == 0)
            throw new EmptyQueueException();
        return elementAt(0);
    }
    /**
     * Returns where an object is on this queue. 
     *
     * @param   o   the desired object.
     * @return  the distance from the front of the queue where the object is
     *          located; the return value <code>-1</code> indicates that the
     *          object is not on the queue.
     */
    public synchronized int search(Object o) {
        return lastIndexOf(o);
    }
}
--------------------------------------------------------------------------------
/*
 * @(#)EmptyQueueException.java   1.0 2001/02/11
 *
 * (c) Copyright JavaService.Net, 2001. All rights reserved.
 * 
 * NOTICE !      You can copy or redistribute this code freely, 
 * but you should not remove the information about the copyright notice 
 * and the author.
 * 
 * @author  Lee WonYoung  javaservice@hanmail.net, lwy@kr.ibm.com
 * 
 */
package org.jdf.util;
/**
 * Thrown by methods in the Queue class to indicate that the queue is empty
 *
 * @author  Lee WonYoung  javaservice@hanmail.net, lwy@kr.ibm.com
 * @version 1.0 2001/02/11
 */
public class EmptyQueueException extends java.lang.RuntimeException
{
    /**
     * Constructs a new EmptyQueueException with no detail message
     */
    public EmptyQueueException(){
        super();
    }
}
--------------------------------------------------------------------------------
PS: java.util.Stack 소스를 참조했습니다.
================================================
  자바서비스넷 이원영
  E-mail: javaservice@hanmail.net
  PCS:019-310-7324
================================================
제목 : Re: Queue.java : LinkedList
글쓴이: 권기훈(anchor) 2001/04/08 02:18:26 조회수:1395 줄수:5
위에는 Vector를 이용하여 Queue를 구현하셨는데, 이렇게 하면 엘리먼트를
추가하거나 삭제할 때 내부적으로 배열 전체의 복사가 빈번하게 일어나지
않는지요? LinkedList와 같은 자료구조를 활용하는 것은 어떨지 모르겠습니다.
-- from your Anchor
제목 : Re: Queue & Vector 의 성능저하
글쓴이: 최범균(era13) 2001/04/16 17:26:24 조회수:1377 줄수:56
> public class Queue extends java.util.Vector
> {
>     ........
> 
>     /**
>      * Tests if this queue is empty.
>      *
>      * @return  <code>true</code> if this queue is empty;
>      *          <code>false</code> otherwise.
>      */
>     public boolean empty(){
>         return size() == 0;
>     }
>     public synchronized Object enqueue(Object item){
>         addEmement(item);
>         return item;
>     }
>      public synchronized Object dequeue(){
>         Object obj = peek();
>         removeElementAt(0);
>         return obj;
>     }
: 여기서 잠시 주목!!!
removeElementAt(0)은 맨 앞에 있는 요소를 삭제하라는 건데, 이는 Vector를 사용할때
성능에 있어 가장 치명적인 부분입니다. 왜냐면, Vector는 내부적으로 Object 배열을
사용하여 값을 저장하는데, 가장 첫번째 것을 삭제하면 배열에 있는 모든 요소가
한칸씩 앞으로 이동해야 하는 상황이 발생하죠!!! 큐의 특성상 넣으면 반드시 빼게 되어
있는데 이렇게 구현한 경우에는 큐에서 값을 하나 읽어올 때 마다 배열 복사가 일어나게
되죠. 만약 큐에 저장된 요소가 무자게 많다면??? 성능 문제 됩니다.
>
>     ..... // 나머지 코드
>     .,...
> }
> 
그렇다면 Vector와 관련된 문제는 무엇으로 해결해야 하나요? 일단 큐 중간에 저장되어
있는 객체를 검색하는 부분이 반드시 필요한지를 따져야 합니다. 범용적인 큐를 작성하는
것도 좋지만, 특정 어플리케이션에 알맞은 큐를 만드는 게 더 좋을 수도 있거든요.
(어플리케이션의 성능을 향상시키는 데 있어 범용적인 용도로 만든 클래스 만큼 성능에
 있어 치명적인 놈도 없을 겁니다.)
큐의 dequeue()와 enqueue()는 겁나게 많이 사용되는 메소드 중의 하나일 것이 뻔하고
큐의 중간에 어떤 객체가 있는 지 검색하는 메소드는 별로 사용되지 않을 가능성이
많습니다. 이러한 경우에는 배열보다는 링크트 리스트를 사용하는 것이 좋을 것입니다.
이 경우 삽입/삭제와 관련해서 O(1)의 성능을 보이니까요,, 물론, 검색은 링크트리스트의
특성상 배열을 사용하는 경우보다는 느립니다.
추신> 너무나 치졸하게 성능을 따지는 것이 아니냐고 생각하시는 분들도 있겠지만,
      이름도 거창한 퍼포먼스 튜닝은 치졸하게 파고 들어가야 성취할 수 있습니다.
====================================================
고대 전기 전자 연구회 13기
고대 컴퓨터학과 96
한국 최고의 e-마켓플레이스. Tpage 기술연구소
====================================================
제목 : Re: LinkedList 를 이용한 Queue.java
글쓴이: 이원영(javaservice) 2001/04/18 16:57:11 조회수:5125 줄수:138
좋은 지적 감사합니다. java.util.Vector 의 removeElementAt(0) 의 경우,
실제 다음과 같은 로직으로 되어 있네요.
    /**
     * Deletes the component at the specified index. Each component in 
     * this vector with an index greater or equal to the specified 
     * <code>index</code> is shifted downward to have an index one 
     * smaller than the value it had previously. 
     * <p>
     * The index must be a value greater than or equal to <code>0</code> 
     * and less than the current size of the vector. 
     *
     * @param      index   the index of the object to remove.
     * @exception  ArrayIndexOutOfBoundsException  if the index was invalid.
     * @see        java.util.Vector#size()
     * @since      JDK1.0
     */
    public final synchronized void removeElementAt(int index) {
    if (index >= elementCount) {
        throw new ArrayIndexOutOfBoundsException(index + " >= " + 
                             elementCount);
    }
    else if (index < 0) {
        throw new ArrayIndexOutOfBoundsException(index);
    }
    int j = elementCount - index - 1;
    if (j > 0) {
        System.arraycopy(elementData, index + 1, elementData, index, j);
    }
    elementCount--;
    elementData[elementCount] = null; /* to let gc do its work */
    }
System.arraycopy() 가 비록 native 함수로 구현되어 있다손 치더라도, 알고리즘적인
성능저하는 분명히 있을 것으로 추정됩니다.
그래서, LinkedList 를 이용하여 또, 만들어 보았습니다.
------------------------------------------------------------------------------
/*
 * @(#)Queue.java   1.1 2001/04/18
 *
 * (c) Copyright JavaService.Net, 2001. All rights reserved.
 *
 * NOTICE !      You can copy or redistribute this code freely, 
 * but you should not remove the information about the copyright notice 
 * and the author.
 * 
 * @author  Lee WonYoung  javaservice@hanmail.net, lwy@kr.ibm.com
 * 
 */
package org.jdf.util;
/**
 *
 * The <code>Queue</code> class represents a first-in-first-out 
 * (FIFO) queue of objects. 
 *
 * @author  Lee WonYoung  javaservice@hanmail.net, lwy@kr.ibm.com
 * @version 1.0 2001/02/11
 *
 */
public class Queue extends java.util.LinkedList
{
    public Queue(){
        super();
    }
    /**
     * Removes the object at the front of this queue and returns
     * that object as the value of this function. 
     *
     * @return     The object at the front of this queue.
     * @exception  EmptyQueueException  if this queue is empty.
     */
     public synchronized Object dequeue(){
        Object obj = peek();
        removeFirst();
        return obj;
    }
    /**
     * Tests if this queue is empty.
     *
     * @return  <code>true</code> if this queue is empty;
     *          <code>false</code> otherwise.
     */
    public boolean empty(){
        return size() == 0;
    }
    /**
     * Add an item to the end of the queue. 
     *
     * @param   item   the item to be pushed to the end of this queue.
     * @return  the <code>item</code> argument.
     */
    public synchronized Object enqueue(Object item){
        addLast(item);
        return item;
    }
    /**
     * Looks at the object at the front of this queue without removing it 
     * from the queue. 
     *
     * @return     the object at the top of this queue. 
     * @exception  EmptyQueueException  if this queue is empty.
     */
    public synchronized Object peek() {
        if ( size() == 0 )
            throw new EmptyQueueException();
        return getFirst();
    }
    /**
     * Returns where an object is on this queue. 
     *
     * @param   o   the desired object.
     * @return  the distance from the front of the queue where the object is
     *          located; the return value <code>-1</code> indicates that the
     *          object is not on the queue.
     */
    public synchronized int search(Object o) {
        return indexOf(o);
    }
}
----------------------------------------------------------------------------
PS: JDK 1.2 에서만 가능하겠네요... JDK 1.1 에서도 동작케 하려면, 
java.util.LinkedList 와 유사한 구현을 직접 해주어야 하는 점이
다소 아쉽네요.
================================================
  자바서비스넷 이원영
  E-mail: javaservice@hanmail.net
  PCS:019-310-7324
================================================
제목 : Re: Queue.java
글쓴이: 이홍래(zoolook) 2001/05/22 15:26:26 조회수:965 줄수:6
다른 분들과 좀 다른 관점에서 봐서 그런데 상속보다는 delegation 을 이용하는게 더 낫지
않을까여?
Vector의 type을 가져간다는게 좀...
Java에서 Stack도 같은 방식으로 구현되어 있는데 개인적으로 좀 잘못되지 않았나...
생각하거든요. ^^ 흐...
다른 분들은 어떻게 생각하나 싶어서 몇 자 적어 봤슴당.
제목 : Re: Queue: delegation & blocking mode feature
글쓴이: 이원영(javaservice) 2002/04/02 05:40:01 조회수:989 줄수:155
------------------------------------------------------------------------------
/*
 * @(#)Queue.java   1.1 2002/04/01
 *
 * (c) Copyright JavaService.Net, 2002. All rights reserved.
 *
 * NOTICE !      You can copy or redistribute this code freely, 
 * but you should not remove the information about the copyright notice 
 * and the author.
 * 
 * @author  Lee WonYoung  javaservice@hanmail.net, lwy@kr.ibm.com
 * 
 */
package org.jdf.util;
/**
 *
 * The <code>Queue</code> class represents a first-in-first-out 
 * (FIFO) queue of objects. 
 *
 * @author  Lee WonYoung  javaservice@hanmail.net, lwy@kr.ibm.com
 * @version 1.1 2002/04/01
 *
 */
public class Queue
{
    private java.util.LinkedList list = new java.util.LinkedList();
    private boolean blocking_mode = false;
    /**
     *
     * @param  mode  blocking mode flag. default is false
     */
    public Queue(boolean mode){
        this.blocking_mode = mode;
    }
    /**
     * This method is exactly the same as Queue(false);
     */
    public Queue(){
        this(false);
    }
    /**
     * Removes the object at the front of this queue and returns that object as
     * the value of this function. 
     * EmptyQueueException will be thrown if this queue is empty when the blocking
     * mode is false.
     * Or this method will block if queue is empty and the blocking mode is true.
     * if timeout value is greater than 0, this method will block only until
     * specified amount of timeout value.
     *
     * @param      timeout  specified amount of wait timeout(ms) . 
     *                      If blocking_mode is false, it will be ignored.
     *                      And if blocking_mode is true and timeout is 0,
     *                      this method will always block until queue is not
     *                      empty.
     *                      
     * @return     The object at the front of this queue.
     * @exception  EmptyQueueException  if this queue is empty when the blocking
     *             mode is flase.
     *             
     */
     public synchronized Object dequeue(long timeout){
        if ( blocking_mode ) {
            long s = System.currentTimeMillis();
            long _timeout = timeout;
            while ( list.size() == 0 ) {
                if ( timeout == 0L ) try{ wait(); }catch(Exception e){}
                else {
                    try{ wait(_timeout); }catch(Exception e){}
                    _timeout = timeout - (System.currentTimeMillis() - s);
                    if ( _timeout < 10L ) 
                        throw new TimeoutException("wait time expired (" + timeout + ")");
                }
            }
        }
        Object obj = peek();
        list.removeFirst();
        return obj;
    }
    /**
     * This method is exactly the same as dequeue( 0L );
     */
     public synchronized Object dequeue(){
        return dequeue( 0L );
    }
    /**
     * Tests if this queue is empty.
     *
     * @return  <code>true</code> if this queue is empty;
     *          <code>false</code> otherwise.
     */
    public boolean empty(){
        return list.size() == 0;
    }
    /**
     * Add an item to the end of the queue. 
     *
     * @param   item   the item to be pushed to the end of this queue.
     * @return  the <code>item</code> argument.
     */
    public synchronized Object enqueue(Object item){
        list.addLast(item);
        if ( blocking_mode ) notifyAll();
        return item;
    }
    /**
     * Looks at the object at the front of this queue without removing it 
     * from the queue. 
     *
     * @return     the object at the top of this queue. 
     * @exception  EmptyQueueException  if this queue is empty.
     */
    public synchronized Object peek() {
        if ( list.size() == 0 )
            throw new EmptyQueueException();
        return list.getFirst();
    }
    /**
     * Returns where an object is on this queue. 
     *
     * @param   o   the desired object.
     * @return  the distance from the front of the queue where the object is
     *          located; the return value <code>-1</code> indicates that the
     *          object is not on the queue.
     */
    public synchronized int search(Object o) {
        return list.indexOf(o);
    }
}
-----------------------8><----------------------------------------------
Note: You have to make EmptyQueueException and TimeoutException
public class EmptyQueueException extends java.lang.RuntimeException
public class TimeoutException extends java.lang.RuntimeException
-------------------------------------------------------  
  본 문서는 자유롭게 배포/복사 할 수 있으나 반드시
  이 문서의 저자에 대한 언급을 삭제하시면 안됩니다
================================================
  자바서비스넷 이원영
  E-mail: javaservice@hanmail.net
  PCS:011-898-7904
================================================


 

 

 


 

 

참고: SocketPooling기법.doc

 

  본 문서는 자유롭게 배포/복사 할 수 있으나 반드시 이 문서의 저자에 대한 언급을 삭제하시면 안됩니다

================================================================================================
  자바서비스넷 이원영  E-mail: javaservice@hanmail.net   PCS:019-310-7324
================================================================================================

 

SocketPooling기법.doc
0.19MB