'SCJD'에 해당되는 글 6건

  1. 2007.12.24 SCJD-5.검색과 레코드 Locking

 

확장 데이타베이스(MovieDatabase 클래스)에서 기본 데이타베이스(Movie 클래스)에 추가된

기능은 레코드 검색기능입니다.

 

MovieDatabase 클래스는 public List finds(String query) 메소드로 검색기능을 지원합니다.

레코드 검색식인 query "name=value,name=value,name=value" 형태로 이루어진 조건식

인데,칼럼 이름이 "name", 칼럼 값이 "value"인 레코드들을 찾습니다.

검색으로 찾는 레코드는 모든 검색 조건을 만족하는(AND 조건) 레코드만 해당됩니다.

 

데이타베이스는 public List finds (String query) 메소드 호출을 받으면 데이타베이스 파일의

레코드를 모두 읽어서,레코드 칼럼이름과 값을 쌍으로 해서 Map에 넣어둡니다.

물론 물리적인 데이터베이스 파일에 존재하는 삭제된 레코드는 제외합니다.

그 다음 Map에 있는 레코드가 query 조건에 적합한지 그렇지 않은지를 검사합니다.

query 조건은 "name=value,name=value"등으로 이루어져 있으므로 일단 ","를 기준으로

검색조건 토큰을 만듭니다. "name=value"를 하나의 검색 토큰으로 생각합니다.

검색 토큰은 "=" 를 기준으로 칼럼 이름과 값으로 나누어지므로 이를 분리합니다.

이제 Map의 레코드와 검색 토큰의 "name","value"를 비교합니다.

 

검색 토큰의 "value"의 값이 "any" 인 경우는 모든 레코드가 만족하므로 검색 토큰을 통과

하여 다음 검색 토큰을 이용합니다.

검색 토큰의 "value" "any"가 아닌 경우는 Map에 있는 레코드 "name" 칼럼값과 

"value"과 다른 경우는 그 레코드는 검색 조건에 맞는 레코드가 아닙니다.

, 리턴값으로 돌려주어서는 안되는 레코드입니다.

하지만 Map에 있는 레코드의 "name"칼럼값과 검색 토큰의 "value"값이 일치하는 경우는,

조건 검색에 적합한 레코드일 가능성이 있는 레코드이므로 다음 검색 토큰을 검사합니다.

다음 검색 토큰 역시 이전과 같은 방법으로 비교를 하는데,조건에 맞지 않는 레코드이면

리턴하지 않고, 조건에 맞는 레코드이면 다음 검색 토큰을 검사합니다.

이런식으로 검색 조건의 마지막 검색 토큰 까지 검사할 것이고, 마지막 검색 토큰을 적용한

다음에는 NoSuchElementException이 발생합니다.

검색 토큰을 계속 검사하다가 발생한 NoSuchElementException은 검색 조건 query

의 모든 검색 토큰과 일치하는 레코드라는 뜻이므로,조건 검색 값과 Map의 레코드 실제 값

이 같다는 뜻입니다. 즉 이 레코드는 조건 검색에 해당하는 레코드로 리턴해주어야 합니다.

 

레코드 Lock, 레코드 Unlock

확장 데이타베이스(MovieDatabase)에서 구현하는 레코드 Lock 기능의 핵심은 경쟁하는

Thread들이 특정레코드를 동시에 읽고,쓰고자 할때 이 Thread들을 어떻게 Serialization

시킬것인가 하는 문제입니다. , 2개 이상의 Thread가 동시에 같은 레코드를 읽고,쓰게

하지 않고, Thread가 읽고,쓰기를 마친 후에 다른 Thread가 읽고,쓰기를 할 수 있도록

제어합니다. 물론 2개 이상의 Thread가 다른 레코드를 동시에 읽고,쓰는 것은 전혀 문제가

없어야 합니다. Lock의 단위가 데이터베이스 전체가 아니라 레코드 단위이어야 합니다.

 

확장 데이타베이스(MovieDatabase 클래스)는 레코드 Lock을 구현하기 위해서 두개의

메소드를 도입했습니다.

public void lock(int recno) public void unlock(int recno)이 그것 입니다.

한 메소드는 특정 레코드 recno Lock을 얻으려는 메소드이고,다른 메소드는 특정 레코드

recno에 대한 Lock을 해제하려는 메소드입니다.

반드시 명심해야하는 점은 Lock을 소유하고, 해제하는 주체는 Thread라는 사실입니다.

 

MovieDatabase 클래스에서 레코드 Lock을 구현하기 위한 두개의 메소드를 살펴보면

두개의 메소드가 공유하는 객체가 있는데 Hashtable형 멤버변수 lockTable 입니다.

lockTable 객체는 데이터베이스에 존재하는 레코드를 배타적으로 사용중인 Thread

레퍼런스를 갖고 있습니다. (레코드 Lock을 가진 Thread의 레퍼런스를 갖고 있습니다.)

Hashtable의 키으로는 레코드의 번호를(Wrapper클래스인 Integer객체를 사용합니다.)

값으로는 레코드를 Lock하고 있는 Thread 입니다.

 

Thread들이 레코드의 Lock을 얻고, 레코드의 Lock을 해제하는 행동을 lockTable

연계하여 예를 들어보겠습니다.

Thread A가 데이터베이스의 특정 레코드 N Lock 한다고 했을 때, Thread A는 반드시

특정 레코드 N이 제 3 Thread에 의해서 Lock 되어 있는지 검사해야만 합니다.

만약 특정 레코드 N이 제 3 Thread에 의해서 Lock되어 있지 않다면 Thread A

특정 레코드 N에 대한 Lock을 얻은 것으로 간주합니다.또는 특정 레코드 N이 자기자신에

의해서 이미 Lock되어 있다면 Thread A는 특정 레코드 N에 대한 Lock을 얻은 것으로

간주합니다.  Thread A는 특정 레코드 N Lock을 소유한 Thread가 자기 자신이라는 것을

표시합니다.

 

이 사건을 lockTable과 관련하여 설명하면 Thread A가 특정 레코드 N Lock 하려고

했을때 Thread A는 반드시 lockTable에 키 N으로 값이 있는지 검사합니다.

만약 lockTable의 키 N에 대해서 값이 존재하지 않으면(혹은 그 값이 자기자신에

대한 레퍼런스라면) Thread A Lock을 얻은 것으로 간주하고, Lock을 소유한 Thread

자신이라는 것을 알리기 위해 Thread A lockTable의 키 N에 대해서 자신의 레퍼런스

(Thread 레퍼런스)를 값으로 하여 lockTable에 저장합니다.

 

Thread A가 데이타베이스의 특정 레코드 N Lock 한다고 했을 때, Thread A

반드시 특정 레코드 N이 제 3 Thread에 의해서 Lock 되어 있는지 검사해야

하는데, 이때 이미 제 3 Thread가 특정 레코드 N에 대해서 Lock을 갖고 있다면

Thread A는 제 3 Thread Lock을 해제 할 때까지 기다려야 합니다.

 

Thread 들이 레코드의 Lock을 얻기 위해서는 반드시 lockTable 객체에 저장되어 있는

레코드 Lock 정보를 이용해야만 하는데, lockTable의 정보는 키로는 레코드 번호, 값으로는

레코드 Lock을 가지고 있는 Thread로 이루어져 있습니다.

데이터베이스의 레코드를 Lock 하려는 경쟁하는 Thread들은 레코드 Lock정보를 우선 읽어

야 하므로 lockTable 경쟁적으로 읽고,쓰려고 할 것입니다. 경쟁하는 Thread들이 읽고,쓰려

는 공유변수는 반드시 Serialization을 해주어야 하듯이 MovieDatabase에서는 lockTable

객체를 synchronized 블록으로 경쟁하는 Thread로부터 보호합니다.

 

예제 18 - 9 처럼 synchronized 블록에 lockTable을 선언하면, lockTable Lock을 얻은

1개의 Thread synchronized 블록을 입장할 수 있습니다.

synchronized(lockTable) {        

        Thread thread = (Thread)lockTable.get(new Integer(record));

          if (thread == null) {             

                     lockTable.put(new Integer(record),Thread.currentThread());

            return;

        }

          else if (thread == Thread.currentThread()) {    

            return;

      }

        else {

try {

                                                     lockTable.wait();                

            }

            catch(InterruptedException ie) {

                throw new IOException(ie.getMessage());

            }

          }

             }

예제 18 - 9

 

특정한 레코드를 Lock하려고 예제 18 - 9 synchronized 블록안에 진입한 Thread에게는

다음의 3가지 경우 밖에 없습니다.

첫째는 특정 레코드를 Lock 한 제 3 Thread가 없는 경우입니다.

둘째는 특정 레코드를 Lock Thread가 자기자신인 경우입니다.

세째는 특정 레코드를 Lock 한 제 3 Thread가 있는 경우입니다.

 

첫째 경우는 레코드에 대한 Lock 정보를 보관하는 lockTable 객체에 특정 레코드 번호로

값을 구해보면 그 값이 없는 경우 즉,값이 null 인 경우 입니다.

이 경우는 특정 레코드에 대한 Lock을 제 3 Thread가 가지지 않은 경우이므로

특정 레코드에 대한 Lock을 가진 것으로 간주하고 자유롭게 읽고,쓰고,고치고 할 수

있습니다. 다만 제 3 Thread에게 특정 레코드(recno번호 레코드) Lock을 가진 Thread

자신이란 표시를 반드시 합니다.

lockTable에 특정 레코드 번호를 키로해서 자기 자신에 대한 레퍼런스를 저장합니다.

 

둘째 경우는 특정 레코드에 대해서 public void lock(int recno)를 연속으로 호출한 경우가

있을 수 있습니다. 이 경우는 이미 자신이 특정레코드에 대해서 Lock을 가지고 있는 상태

이기 때문에 자유롭게 특정 레코드를 읽고,쓰고,고치고 할 수 있습니다.

 

셋째 경우는 가장 골치아픈 경우입니다.  3 Thread가 이미 특정 레코드 Lock

가지고 있다고 간주되는 상황입니다. 이 경우는 선택의 여지가 없습니다.

특정 레코드에 대해서 읽고,쓰거나 고칠수 없습니다. 읽고,쓰거나 고칠수 없다고 표현하면

오해 하는 분들이 가끔 계신데 Thread가 물리적으로 읽고,쓰거나,고칠수 없는게 아닙니다. 물리적으로는 얼마든지 읽고,쓰거나 고칠수는 있지요.

하지만 특정 레코드에 대해서 Lock을 가지지 않은 Thread가 레코드를 읽고,쓰거나,고치면

데이터베이스의 내용이 안전하지 않습니다. 무결성이 지켜지지 않을 수 있습니다.

그래서 레코드를 Lock하려는 Thread가 셋째인 경우인때는 논리적으로,아주 고의적으로

레코드를 읽고,쓰거나 고칠지 않는 겁니다. 이때 Thread가 해야할 일은 단 하나,

레코드 Lock을 갖고 있는 제 3 Thread가 레코드 Lock을 해제 할 때까지 기다리는

것입니다. 3 Thread가 언젠가는 레코드 Lock을 해제한다고 믿으면서 말입니다.

 

하지만 안타깝게도 제 3 Thread Lock을 해제할때 까지 기다리는 것도 쉽지가 않습니다.

무슨말인가 하면 synchronized 블록에 진입해 있는 현재의 Thread가 세째 경우인 때는

레코드 Lock을 갖지 못해서 레코드를 읽고,쓰지 못하지만 적어도 lockTable객체에 대한

Lock은 갖고 있습니다. (Thread가 현재 synchronized 블록안에 진입해 있는 것은 확실하기

때문입니다.)

Thread는 레코드를 읽고,쓰고자 한다면 레코드 Lock을 가져야 하고, 레코드 Lock을 얻기

위해서는 레코드 Lock 정보를 보관하고 있는 lockTable 객체를 읽어야합니다.

데이터베이스의 레코드에 대한 Lock정보를 lockTable에 저장해놓았기 때문이지요.

 

MovieDatabase는 경쟁하는 Thread가 공유하는 lockTable 변수을 동시에 읽고,쓸수 없도록

lockTable synchronized 블록으로 보호합니다. 그래서 lockTable을 읽고,쓰는 Thread

항상 lockTable Lock을 갖고 있어야 합니다.

synchronized 블록안에 진입한 Thread(lockTable  Lock을 가진 Thread)가 레코드 Lock

을 얻지 못하는 세째 경우인때는 당연히 lockTable Lock을 포기해야만 합니다.

자신은 레코드 Lock을 가지지 못해서 레코드의 Lock이 해제되기를 기다려야만 하는

운명인데, lockTable Lock을 움켜 잡고 있으면 제 3 Thread는 레코드 Lock

얻기는 커녕 lockTable을 읽을 수 있는 기회 조차 없으니까 말입니다.

이를 위해서 Thread lockTable객체의 wait()메소드를 호출하여,lockTable Lock

포기하고, 3 Thread lockTable을 읽을 수 있도록 합니다.

언젠가 lockTable Lock을 가지는 제 3 Thread lockTable객체의 notify() 메소드를

호출하면 lockTable객체의 wait() 상태에서 벗어나 lockTable Lock을 얻을 수 있는

기회가 다시 생깁니다.

 

레코드 Lock의 해제는 레코드 Lock을 얻으려고 할때보다는 매우 간명합니다.

Thread가 갖고 있는 레코드 Lock을 해제하는 것인데, Thread가 레코드 Lock을 해제할때는

우선 레코드 Lock 정보를 보관하는 lockTable을 읽은후(독점적으로 읽습니다.) 레코드 Lock

을 해제한 것으로 간주하고,lockTable에 레코드 Lock을 갖고 있는 Thread가 자기 자신인

것을 삭제합니다. 레코드 Lock을 해제하는 것이 대단한 것이 아니고, Hashtable

lockTable에 레코드에 대한 값을 넣었다고,지웠다가 하는 것이 전부이니까요.

 

만약 Lock을 해제하려는 레코드의 키에 대한 값이 자기자신이라면 자신이 Lock을 갖고

있는 레코드이므로 lockTable에 있는 레코드의 키와 값을 지워버린후에 lockTable

읽고,쓰려고 기다리는 Thread들에게 기회를 주기위해 lockTable notifyAll() 메소드를

호출합니다. 해제하려는 레코드의 키에 대한 값이 자기자신이 아니라면 단순하게

lockTable notifyAll() 메소드만 호출하여 lockTable을 읽고,쓰려고 기다리는 Thread들에게

기회를 줍니다.

 

package movie;

 

import java.io.*;

import java.util.*;

 

/**

 * 이 클래스는 데이타베이스 기능인 레코드 검색,추가,삭제,수정등의 서비스를 제공합니다.

 * 영화를 예매할 수 있는 비즈니스 로직을 포함합니다.

 *

 * @author djkim

 * @version 1.0

 * @since 2003.05

 */

public class MovieDatabase extends Movie {

    /**

     * 특정 레코드를 참조하고 있는 thread에 대한 정보를 가지고 있습니다.

     * lockTable key thread가 현재 참조하고 있는 레코드의 번호이고

     * lockTable key에 대한 값은 레코드를 참조하고 있는 thread 자체입니다.

     */

             private static Hashtable lockTable = new Hashtable();

 

 

    /**

     * 데이타베이스 파일 dbname을 엽니다.

     * 데이타베이스의 구성은 long형 데이타베이스 헤더정보길이,

     * int형 레코드의 갯수,ing Field의 갯수,Field 정보 즉 칼럼 이름,칼럼 길이

     * 그리고 실제 레코드들로 구성됩니다.

     *

     * @param dbname 데이타소스의 이름

     * @exception IOException 데이타베이스 파일이 존재하지 않을때*/

    public MovieDatabase(String dbname) throws IOException {

                           super(dbname);

             }

            

    /**

     * 데이타베이스 파일 dbname을 구성합니다.

     * 데이타베이스의 구성은 long형 데이타베이스 헤더정보길이,

     * int형 레코드의 갯수,ing Field의 갯수,Field 정보 즉 칼럼 이름,칼럼 길이

     * 그리고 실제 레코드들로 구성됩니다.

     *

     * @param dbname 데이타베이스 파일의 이름

     * @param fields 데이타베이스의 레코드 칼럼 정보

     * @exception IOException 데이타베이스 파일이 이미 존재할경우

     */

    public MovieDatabase(String dbname, Field[] fields) throws IOException {

                           super(dbname,fields);

             }

 

             /**

             * query에 따라 데이타베이스를 검색하여 조건에 맞는 레코드를 리턴합니다.

             * 조건에 맞는 레코드가 데이타베이스에 없다면 null을 리턴합니다.

             * qeury comma로 분리된 name=value,name=value형태입니다.

             * 예를 들어 query가 제목=반지의제왕,극장=명보극장 이라면 명보극장에서

             * 상영하는 제목이 반지의제왕인 레코드들을 검색합니다.

             *

             * @return List 검색조건에 맞는 레코드들

             * @param query  레코드 검색조건, comma로 분리된 name=value형태의 반복입니다.

             * @exception DatabaseException 데이타베이스 파일을 읽을 수 없을때

             */

    public synchronized List finds(String query)

        throws DatabaseException

    {

        List list = new ArrayList();     // 검색조건에 해당되는 레코드을 담습니다.

        int  count = getRecordCount();

       

        /* 첫번째 위치 레코드부터, 레코드 갯수만큼 순차적으로 검색합니다.*/

        for(int i = 1; i <= count; i++) {

       

            Record record = getRecord(i);                    // i 번째 레코드를 찾습니다.

            if (record.getFlag() == Movie.DEAD)           // 삭제된 레코드

                continue;

                    

 

            Map ct = new HashMap();                       

 

            String values[] = record.getValues();         // 레코드의 값

           

            /* 레코드 칼럼이름을 key , 찾은 레코드의 값을 value Map을 구성합니다.*/

            for(int j = 0; j < fields.length; j++) {

                String name = fields[j].getName().trim();             // 레코드 칼럼 이름

                String value = values[j].trim();                                        // 레코드 칼럼 값

                ct.put(name,value);

            }

           

           

            try {

                StringTokenizer st = new StringTokenizer(query,",");

               

                while (true) {

                   /* name=value의 검색 토큰 */

                    String token = st.nextToken();                     

                    int index = token.indexOf('=');

                   

                    /* query에서 지정한 칼럼이름 */

                    String name = token.substring(0,index).trim();            

                    /* query에서 지정한 칼럼값 */

                    String value = token.substring(index+1).trim();           

                   

                    /* query에서 지정한 칼럼값이 any이면 레코드의 칼럼값이

                       어떤것이어도 상관없음 */

                    if (value.equalsIgnoreCase("any"))             

                        continue;                

                       

                                                                  /* query 칼럼의 값과 i번째 레코드의 칼럼값이 다르면

                                                                   i번째 레코드는 검색결과에 포함되지 않음 */

                    if ( !value.equals(ct.get(name)))  

                        break;                                  

                }

            }

            catch (NoSuchElementException e) {                      

                     /* query name=value 쌍을 모두 조사해볼 동안

                      query에서 지정한 칼럼의 값과 i번째 레코드가 가지고 있는 칼럼의 값이

                      다른 경우는 없으므로 i번째 레코드를 검색결과에 포함 */

                list.add(record);                   

            }

           

        }

        return list;

    }

 

    /**

    * 이 메소드는 지정한 레코드(영화)에 대해 하나 이상의 표를 예매합니다.

    * 이 메소드는 다중 thread 환경에서 사용되므로 Locking기능을 반드시

    * 포함해야만 합니다. 조건에 맞는 영화표가 없을 경우는 적절한 메세지를 포함한

    * DatabaseException throw 합니다.

    * @param recno : 예약하고자 하는 영화에 대한 레코드 번호 (레코드 위치)

    * @param seats : 예약하고자 하는 영화표 갯수

    * @exception DatabaseException 예약하고자 하는 영화표가 부족할때

             *

             */

             public void book(int recno,int seats) throws DatabaseException {

                           try {

                                        Record record = getRecord(recno);    // recno 번호(위치) 레코드          

                                        String[] values = record.getValues();// 레코드의 칼럼 값         

                                        int availableSeats;

                          

                                        /* 레코드의 9번째 칼럼값이 현재 남아 있는 표를 의미합니다. */

                                        availableSeats = Integer.parseInt(values[8].trim());       

                                       

                                        /* 예매하려는 표가 남아있는 표보다 많은 경우 */

                                        if (seats > availableSeats)  {                        

                                                     String msg = "죄송합니다. " + availableSeats + " 표 밖에 없습니다!!!"        ;

                                                     throw new DatabaseException(msg);

                                        }

                                        int remains = availableSeats - seats;// 예매표를 발행하고 남은 영화표 갯수

                                        values[8] = String.valueOf(remains); 

                                        record.setValues(values);   // 레코드의 9번째 칼럼값인 영화표갯수 정보 수정

                                        modify(record);                                             // 레코드정보 overwrite (수정)

                           }

                           catch(NumberFormatException nfe) {

                                        throw new DatabaseException(nfe.getMessage());

                           }

             }

 

    /**

    * 요청한 레코드 번호(위치)에 대한 lock을 얻습니다.

    * 이 메소드는 lock을 얻을때까지 영원히 기다립니다.

    *

    * @param record  Locking 할 레코드번호

    * @exception IOException Locking할 레코드 번호가 잘못된 번호일 때

    */

    public void lock(int record) throws IOException {

                 if (record < 0 || record > getRecordCount()) {

            throw new IOException("레코드 번호 " + record + "가 잘못되었습니다!!!");

        }

        /* record번째 레코드에 대한 lock을 얻기까지 영원히 시도합니다. */

        while(true) {         

                                        /* record번째 레코드에 대한 lock에 대한 정보는 lockTable에 들어있습니다.

         record번째 레코드에 대해서 lock을 얻고자 하는 thread

                                         lockTable에 접근해서 lockTablekey로 값을 구해서

         그 값이 null 이거나 (lock을 얻은 thread가 없음)

         혹은 그 값이 null이 아니더라도 그 값이 thread 자기자신이라면

         record번째 레코드에 대한 lock을 가진 thread가 자기자신이라는

                                         뜻이므로 진행해도 좋습니다.

         반면 그 값이 null이 아니고, 자기자신도 아니라면 제 3 thread

         record번째 레코드에 대해서 lock을 얻었으므로, 3 thread

         record번째 레코드의 lock을 해제할때 까지 기회를 기다립니다.

         현재 thread record 번째 레코드에 대한 lock을 얻을 수 있을 지 여부는

         lockTable에 담겨 있고, 3의 경쟁하는 thread lockTable을 읽고,

                                         쓰려고 하기 때문에

         반드시 lockTable을 읽고,쓰기 위해서는 lockTable객체 자체에 대한 lock

         얻어야합니다. 이런 이유로 lockTable synchronized 블록으로

                                         보호했습니다.

         일단 synchronized 블록으로 진입한 thread record번째 레코드에 대한

         lock을 얻은 경우이든, 그렇지 않은 경우이든 일정한 시간안에

                                         synchronized블록을 빠져나오게 되어 있습니다.

          3 thread record번째 레코드에 대한 lock을 이미 갖고 있어서

         lock을 얻지 못한 경우는 lockTable.wait()를 호출함으로써

         lockTable객체에 대한 lock을 해제하게 됩니다.

                                         lockTable객체의 lock을 얻으려는 thread에게 기회를 주게 됩니다.

                                        */

 

                                        synchronized(lockTable) { 

                                                     /* record Integer객체로 wrapping key값으로 record번째 레코드에

                                                     대한 lock를 얻은 thread정보를 얻습니다. */

                     Thread thread = (Thread)lockTable.get(new Integer(record));

                    

                     /* record번째 레코드에 대해서 lock을 가진 thread가 없음 */

                     if (thread == null) {             

                                  /* record번째 레코드에 lock을 자신이 가졌다는 정보를 저장한후

                                   synchronized 블록 빠져나감 */

                   lockTable.put(new Integer(record),Thread.currentThread());

                    return;

                }

                else if (thread == Thread.currentThread()) {       

                   /* record번째 레코드의 lock을 이미 가지고 있으므로

                    synchronized 블록 빠져나감 */

                    return;

                }

                else {      

                   /* record번째 레코드에 대해서 제 3 thread lock을 가졌음 */

                    try {

                              /* 3 thread record번째 레코드에 대해서 lock을 해제 할

                               동안 기다림.

                                3 thread lockTable에서 record key값을 이용해서 값을

                               삭제한후 기다리는 자기(thread)를 위해 lockTable.notify()

                               호출해 주기를 기다림 */

                                                                                lockTable.wait();                

                    }

                    catch(InterruptedException ie) {

                        throw new IOException(ie.getMessage());

                    }

                }

            }

        }

    }

 

    /**

    * 요청한 레코드에 대한 lock을 해제합니다.

    *  thread가 요청한 레코드에 대해서 lock을 가지고 있지 않은 경우는

    * lock을 해제하는 요청을 단순히 무시합니다.

    * @param record Locking 할 레코드번호

    */

    public void unlock(int record) {

 

                           /* 경쟁하는 thread가 읽고,쓰려고 경재하는 객체 lockTable

                            읽고,쓰고 있으므로 synchronized 블록으로 보호    */

                           synchronized(lockTable) {

        Thread thread  = (Thread)lockTable.get(new Integer(record));

       

        if (thread == Thread.currentThread()) {           

                                                     /* record번째 레코드의 lock 소유자가 자기자신인 경우

                                                      record번재 레코드의 lock정보, 즉 자기자신이 lock를 가졌다는

                                                      정보를 삭제 */

                     lockTable.remove(new Integer(record));

        }

        /* lockTable객체의 lock을 얻고자하는 thread에게 기회를 줌 */

                                        lockTable.notifyAll();          

                           }

    }

}

예제 18 - 10 MovieDatabase.java

 

비즈니스 메소드인 public void book(int recno,int seats)은 데이터베이스 레코드 recno

seats만큼 예매하는 메소드인데, recno 번호 레코드의 남아있는 표의 숫자를 보고,

seats 만큼 줄인다음 , 그 결과를 데이터베이스에 반영하는 것입니다.

recno번호 레코드에 seats보다 적은 좌석이 남아있다면, 예매가 되지 않고 적절한

메시지와 함께 DatabaseException throw 합니다.

Posted by
,