식사하는 철학자 문제 는 동시성 및 자원 공유에 관한 고전적인 문제


동시성 (동시성, Concurrency )은 컴퓨터 과학 에서 시간으로 중복 실행되는 계산 을 수반하는 시스템의 특성이며, 이러한 계산은 자원을 공유 할 수있다. 에드가 다이크 스트라 에 따르면, "두 개 이상의 실행 흐름이 동시 병행 될 때 동시성이 나온다"고했다. 공유 자원의 동시 사용에 따라 다양한 문제가 발생한다. 공유 자원에 관한경쟁 상태 ​​는 예측할 수없는 시스템 상태를 발생시킨다. 상호 배제 의 도입으로 경쟁 상태는 막을 수 있지만 교착 상태 나 리소스 부족을 등의 문제가 새롭게 발생한다. 병렬 시스템의 설계에서는 응답 시간을 최소화하고 처리량을 최대화하기 위해, 데이터 교환, 메모리 할당, 실행 일정 등에 관련된 기술의 신뢰성을 추구한다.

목차

  [ 숨기기 ] 

이론 편집 ]

동시성 이론은 1960 년대 에 칼 아담 페트리 가 독창적 인 페트리 연구를 실시한 이래, 이론적 컴퓨터 과학 의 주요 연구 영역이다. 이후 병렬 시스템을 이해하기위한 다양한 이론 모델, 논리 도구가 개발되어왔다.

모델 편집 ]

모델 개발과 병행 시스템의 이해를위한 형식 방법에는 여러 가지가 개발되어왔다. 다음에 중요한 것은 열거한다.

이러한 동시성 모델의 일부는 먼저 추론과 사양 기술을 목적으로하고있다. 한편, 다른 모델은 전체 개발주기 (병행 시스템의 설계, 구현, 시험, 테스트, 시뮬레이션)을 지원하는 것을 목적으로하고있다.

논리 편집 ]

다양한 때 상 논리 가 동시에 시스템의 이해를 돕기 위해 사용된다. 특히 선형시 상 논리 와 계산 나무 논리 는 동시에 시스템의 각 시점의 상태를 확인하는 데 사용 가능하다. Action Computational Tree Logic과 Hennesy-Miller Logic, 레슬리 란뽀토 의 Temporal Logic of Actions 등은 "액션 (상태 변화)"의 순서를 확인할 수있다. 이러한 논리의 주된 용도는 병렬 시스템의 사양을 설명하기위한 것이다.

실제 병렬 처리 편집 ]

이 절은 쓰기의 길입니다 이 절은 집필 중 입니다. 가필 정정 해 주시 협력자를 요구하고 있습니다 .

병렬 프로그래밍 은 병렬 시스템을 구현하는 데 사용되는 프로그래밍 언어와 알고리즘으로 구성된다. 일반적으로 병렬 프로그래밍은 병렬 프로그래밍 보다 범위가 넓은으로 다양한 형태의 통신 및 상호 작용에 관계한다. 반면 일반 병렬 시스템은 미리 정해진 갖춘 구성의 통신 패턴을 갖추고있다. 병렬 프로그래밍의 기본 목표는 " 정당성 ","성능 ","견고 "이 포함된다. 운영 체제와 같은 병렬 시스템은 반영구적으로 동작하는 것이 요구되고있다. 일부 병행 시스템은 투명 동시성을 갖게 구현되어있다. 그 경우, 병렬로 작동하는 계산 주체 군은 공유 자원을 사용하지만, 프로그래머는 그 구현은 숨겨져있다.

병렬 시스템은 공유 자원을 사용하기 때문에 공유 리소스에 대한 액세스를 제어하기위한 일종의 조정 을 할 것 (일반적으로 하드웨어)를 구현할 필요가있다. 따라서 무제한 비결정 가 도입 된 병렬 시스템의 정당성과 성능에 영향을 준다.

관련 항목 편집 ]

외부 링크 편집 ]






식사하는 철학자 문제 ( Dining Philosophers Problem )는 병렬 처리 문제를 일반화 한 예이다. 고전적인 멀티 프로세스 의 동기화 ( 배타 제어 ) 문제이며, 대학 수준의 컴퓨터 과학 과정은 거의 확실 포함되어있다.

1965 년 , 에드가 다이크 스트라 는 5 대의 컴퓨터가 5 개의 테이프 장치에 충돌 액세스하는 동기화 문제를 제시했다. 곧이 문제는 안토니 호아 에서 "식사하는 철학자 문제"변형 말해지는되었다 [1] [2] [3] .

목차

  [ 숨기기 ] 

문제 편집 ]

5 명의 철학자가 식사하거나 걱정거리를하고있다. 그들의 전에 중간에 스파게티가 들어간 큰 공이 놓인 둥근 식탁이있다. 그 식탁에 5 개의 접시가 놓여져 접시와 접시 사이에 포크가 1 개씩 놓여있다.

식사하는 철학자 문제

최근에는 식기를 " 포크 "가 아닌" 젓가락 "으로 소개하는 사례가 늘고있다 [4] . 젓가락이라면, "한 사람이 2 개 확보해야 식사를 할 수 없다"는 제한이 자연이다. 또한 구미 지역에 젓가락 문화가 인정되어 온 것의 표현이기도 것이다. 본고에서는 이전 형식 채 스파게티를 2 개의 포크로 먹기, 한 설정을 그대로 설명한다.

스파게티를 볼에서 가지고 자신의 접시가 될듯에는 2 개의 포크를 필요로하고 철학자는 한 번에 한 개의 포크 밖에 테이블에서 취할 수 없다. (좌우의 손에서 동시에 양쪽에있는 두 개의 포크를 취할 수 없다는 의미. 먼저 양쪽의 포크를 가지고 다음에 반대 측의 포크를 가지고 간다.) 철학자 끼리는 결코 대화를 하지 않는다. 따라서 5 명이 왼쪽 포크를 집어 오른쪽 포크가 식탁에 놓인 것을 기다린다는 위험한 교착 상태가 발생할 가능성이있다.

문제는 어떻게 행동 규율을 설정하면 ( 동시성 알고리즘 ) 각 철학자가 굶어 않아도 하나, 즉 식사와 사색을 영원히 계속 되나이다.

해설 편집 ]

본래 교착 상태 문제 해설 수단으로 사용되었다. 이 시스템이 교착 상태에 이르기는 "채워지지 않은 요구의 원환"가 존재하는 경우이다. 예를 들면, 철학자 1 이 철학자 2 의 손에있는 포크를 기다리고, 2 는 철학자 3 것을 ...... 등등 원 환형 요청이 연쇄한다.

예를 들어 각각의 철학자가 다음과 같이 행동한다.

  1. 왼쪽 포크를 얻을 때까지 사색하고 기다리고 포크를 다룬다.
  2. 오른쪽 포크를 얻을 때까지 사색하고 기다리고 포크를 다룬다.
  3. 식사.
  4. 오른쪽 포크를 둔다.
  5. 왼쪽 포크를 둔다.
  6. 먼저 돌아 반복.

이 해법은 잘못되었습니다. 이 방식으로는 교착 상태가 발생, 전 철학자가 왼쪽 포크가 오른쪽 포크를 갖게되는 것을 기다리는 상황이 발생할 수있는 (이 경우 오른쪽 포크는 결코 얻을 수 없다) 5] .

시점에 따라 한 철학자가 두 포크를 잡히지 않는 상황이 교착 상태와는 별도로 발생한다. 이 자원 부족을 칭하는 (스타베숀은 "기아"이며,이 용어도 "식사하는 철학자 문제"의 비유에 부수 한 농담이 기원이다). 예를 들어 하나의 포크를 잡은 상태에서 다른 포크를 5 분 정도 기다린 후 일단 포크를두고 5 분 정도 기다린 후 다시 식사를 시도하는 규칙을 설정한다. 이 방법은 교착 상태 해결되는 (시스템은 다른 상태로 변화 해 나가는)가 라이브 락 상태는 피할 수 없다. 만약 5 명의 철학자가 정확히 동시에 식탁에 도착했다면, 일제히 왼쪽 포크를 가지고 5 분 오른쪽 포크를 기다리고 왼쪽 포크를 일제히두고 5 분 기다리는 상황이 발생한다.

사용할 포크없는 상태가 실제로 컴퓨터 프로그래밍 공유 자원 의 잠금 상태에 대응한다. 자원 잠금은 한 번에 하나의 프로그램 (또는 코드) 만 해당 리소스에 접근하는 것을 보장하는 수단이다. 있는 프로그램이 원하는 자원이 다른 프로그램에 의해 잠겨있는 경우, 그 프로그램은 잠금되는 것을 기다린다. 여러 프로그램이 작동되는 자원과 관련된 경우, 상황에 따라서는 교착 상태가 발생한다. 예를 들어, 프로그램이 처리를하는데 두 파일을 필요로하고 있다고한다. 그런 두 프로그램이 각각 하나 파일을 잠금하는 경우, 두 프로그램 모두 상대가 잠겨있는 파일을 영원히 기다릴 것이다.

식사하는 철학자 문제는 배타 제어 에 관련된 다양한 문제를 설명하기 위해 일반화 추상화 한 것이다. 철학자들이 식사시 경험하는 다양한 장애는 실제로 컴퓨터 프로그래밍에서 공유 리소스에 독점적으로 액세스 할 수있는 경우의 다양한 어려움에 대응하고있다. 이러한 문제는 병행 계산 분야에서 연구되고있다. 다이크 스트라의 원래의 설문은 테이프 드라이브와 같은 외부 주변 장치에 관한 것이었다. 그러나 식사하는 철학자 문제에서 다루어지고있는 것은 여러 프로세스가 일련의 데이터를 업데이트하려고 할 때 발생하는 문제로 일반화 할 수있다. 운영 체제 의 커널 과 같이 다수의 프로세스가 동시에 작동하는 처리 시스템은 수천 개의 잠금 및 동기화 메커니즘을 사용하여 교착 상태 , 자원 부족을 데이터 파괴 등이 일어나지 않도록 그기구의 사용법을 엄격히 지켜야한다.

해법 편집 ]

웨이터를 배치 해법 편집 ]

비교적 간단한 해법은 식탁에 웨이터를 배치하는 것으로한다. 철학자들은 포크를 손에 잡힐 때 반드시 웨이터의 권한을 가지고한다. 웨이터는 어떤 포크를 사용하고 있는지를 파악하고 있기 때문에 교착 상태를 방지 조정 할 수있다. 4 개의 포크가 사용중인 경우, 마지막 포크 철학자가 요구하면 웨이터가 허용을 기다려야 사용중인 포크가 해제 될 때까지 권한은 주어지지 않는다. 철학자가 항상 오른쪽 포크보다 먼저 왼쪽 포크를 잡는 (또는 반대) 같은 결정두면 이야기는 단순화된다. 웨이터는 세마포어 라는 다이크 스트라가 1965 년에 도입 한 개념처럼 행동 [6] .

구체적으로 설명하기 위해 철학자를 시계 방향으로 A에서 E까지 레이블한다. A와 C가 다이어트에서 4 개의 포크를 사용하고 있다고한다. B는 A와 C 사이에 앉아서, 두 포크도 얻을 수없는 반면, D와 E 사이에 사용하지 않는 포크가 1 개 남아있다. 여기서 D가 식사하고 싶다고한다. 그가 다섯 번째 포크를 타고 교착 상태가 발생할 가능성이있다. 그래서 D가 웨이터에게 포크를 매우 좋은 질문, 기다리도록 지시되면, A 또는 C가 식사를 마치고 포크를 놓으면 적어도 1 명의 철학자가 양손에 포크를 가질 수 것이 확실 된다. 따라서 교착 상태가 일어나지 않는다.

자원 계층의 해법 편집 ]

또 간단한 해법은 자원 (이 경우는 포크)에 반 순서 를 할당하는 방법으로 자원의 요구 순서는 항상 자원의 순서대로하여 자원 해제는 그 역순이다.그리고 순서으로 비 관련 리소스를 한 유닛이 동시에 사용할 수는 없다고한다. 자원 (포크)에 1에서 5까지의 번호를 부여하고, 작동 장치 (철학자)는 항상 번호 젊은 분들의 포크를 먼저 취하고, 그 번호의 큰 포크를 취한다. 개별 철학자가 취하는 포크 번호는 정해져있다. 그리고 포크를 넣으면 번호가 큰 분을 먼저두고 그 번호의 젊은 사람의 포크를 둔다. 이 경우, 5 명의 철학자 중 4 명이 동시에 번호의 젊은 사람의 포크를 들면, 번호의 가장 큰 포크 만 남게되고, 5 번째 철학자는 포크를 취할 수 없다. 또한 그 번호가 가장 큰 포크를 취할 철학자는 1 명 밖에 없기 때문에 그 철학자 만 모두 포크를 들고 식사 할 수있다. 그가 식사를 마치고 포크를 놓을 때 먼저 번호 큰에서두고 이어 번호 젊은 분들의 포크를두기 때문에, 후자의 포크를 기다리고 있던 철학자가 포크를 취하고 식사를 시작하는 된다.

이 해법은 다이크 스트라 가 먼저 제안했지만 한개이다.

자원 계층을 사용한 해법은 교착 상태를 막을 수 있지만, 항상 실용적 못하고, 특히 필요한 자원이 처음부터 전부 파악할 수없는 경우에 유용하지 않다. 예를 들어, 운영 단위가 3 번과 5 번 자원을 가지고 있고, 또한 2 번 자원이 필요할 때 자원 획득 순서를 지키기 위해 5 번과 3 번 자원을 일단 해제하지 않으면 2 번 자원을 획득 할 수 없다. 그러고 3 번, 5 번 순서로 획득 다시. 데이터베이스 의 여러 레코드에 액세스하는 컴퓨터 프로그램이있는 경우, 새로운 레코드에 액세스하기 위해 번호의 레코드를 모두 일단 해제해야한다면, 그다지 효율적으로 작동 할 수없는 것이다.

모니터를 사용한 해법 편집 ]

아래의 코드 예제와 해법은 포크가 명시 적으로 나오지 않는다. 철학자는 양쪽 이웃의 철학자가 식사 중이 아닌 경우에만 식사 할 수있다. 즉, 두 번째 포크를 잡히지 않는 철학자는 반드시 포크를 일단두고 기다리고 다시 1 번째에서 시도하는 방식이다.

포크마다 잠금이 없기 때문에 철학자는 양쪽 이웃의 철학자의 상태에 따라 식사를 시작할지 여부를 결정해야하지만, 그 상태 정보가 오래되지 않은 것을 보증 할 필요가있다. 철학자 2 철학자 1이 규정하지 않은 것을 확인한 다음 철학자 3보기 할 때, 1은 2가 3을보고있는 동안 식사를 시작할지도 모른다. 이 해법은 그런 상황을 방지하기 위해 단일 상호 배타 락을 사용한다. 이것은 특정 포크로 이어지는 것이 아니라, 철학자들의 상태를 변경하는 절차 자체에 대응 한 것이다. 그것이 모니터 하는기구이다. 절차 test , pickup , putdown 모니터에 연결되어 있으며, 1 개의 상호 배타적 잠금을 공유한다. 식사 할 수 있도록되기를 기다리는 철학자는 포크가되지 않는다는 점에주의가 필요하다. 식사하고 싶은 철학자가 있으면 모니터는 첫 번째 포크를 놓지하고 두 번째도 입수 가능하게 된 시점에서 1 번째 다시 시도한다. 식사가 끝나면 철학자는 모니터에 신호를 보내 두 포크가 사용 가능한 상태가 된 것을 알게한다.

또한 예로 든 코드는 자원 부족을 막을 수 없다. 예를 들어, 1 번과 3 번 철학자가 식사 시간이 항상 겹쳐 있으면 2 번 철학자는 계속 수용 할 수 없게된다.

스타베숀을 방지하려면 배고픈 철학자가 식사하지 못한 횟수를 유지하고 그것이 한도를 초과하면 철학자 상태 Hungry 에서 Starving 변경한다. 그리고 포크를 부여할지 여부를 판단 할 때 양쪽 이웃이 모두 Starving 아니다라는 조건을 추가 할 필요가있다.

어느 이웃 Starving 였기 때문에 식사 못했다 철학자는 사실상 그 이웃의 이웃이 식사를 끝낼 때까지 기다릴 것이다. 이러한 종속성이 추가 됨으로써 동시성이 줄어든다. Starving 하는 임계 값을 크게하면, 그 영향을 줄일 수있다.

Chandy / Misra의 해법 편집 ]

1984 년 KM Chandy와 J. Misra는 식사하는 철학자 문제에 다른 해법을 제시했다. 그것은 모든 에이전트 (P 1 , ..., P n )이 모든 자원 (R 1 , ..., R m )를 획득하려고하는 상황에 확장 된 것이다. 다이크 스트라의 해법과 달리 순서화도 선택이다. 그들은이 일반화 된 문제를 다음과 같은 해법으로 해결했다.

  1. 자원을 획득하려고하는 두 철학자의 조합 각각에 대해 포크를 1 개 생성하고 식별 번호 작은 철학자 준다. 이 포크는 dirty 와 clean 의 2 개의 상태가 있고, 초기 상태는 dirty 이다.
  2. 철학자가 일부 리소스를 함께 사용하려는 경우 (즉, 식사하고 싶은 경우), 충돌하는 이웃 포크을 받아야한다. 그런 자신이 가지고 있지 않은 전 포크에 요청 메시지를 보낸다.
  3. 요청 메시지를받은 포크를 가진 철학자있는 포크 clean 이라면 가지고 계속하지만, dirty 이라면 그것을 포기. 포크를 요구 한 측에 보낼 때 그것을 clean 상태로 만든다.
  4. 식사가 끝나자, 포크 dirty 상태가된다. 다른 철학자가 포크를 요구 한 적이 있으면, 그 포크 clean 상태로 보낸다.

이 해법은 대규모 병렬 실행에서도 적용 가능하다.

스타베숀 문제도 해결할 수있다. clean 과 dirty 의 레이블은 가장 오랫동안 식사에 있습니다 붙이지 않은 프로세스를 우선 최근 식사 프로세스의 우선 순위를 낮추는 효과가있다. 철학자가 포크를 놓지 않고 2 회 이상 식사 할 수 없다는 규정을 둔 해법과 비교해 보면, 위에서 언급 한 해법이 더 유연하지만, 경향은 후자와 같다는 것을 알 수있다.

이 분석에서, 그들은 포크 배포 및 clean / dirty 상태로 우선 순위의 시스템을 이끌어 냈다. 그들은이 시스템을 비 환상 그래프로 나타낼지도 모른다 며, 그렇다면, 그 동작은 순환 그래프로 변환 할 수 없게된다. 그것은 교착 상태가 발생하지 않는다는 보장하는 것과 같다. 그러나, 시스템이 먼저 완전히 대칭 상태 (예를 들면, 철학자가 모두 왼쪽 포크를 가지고있는 상태)으로 초기화되는 경우, 그래프는 처음부터 환상이되고 교착 상태를 방지 할 수 없다. 젊은 식별 번호 철학자가 dirty 상태의 포크를 갖게 초기화하면 초기 상태를 비 환상 차트에있다.

해법의 예 편집 ]

다음 코드는 Pascal 로 작성된 해법의 예이다 ( 모니터 를 사용).

PROGRAM d_p ; 
   CONST 
      Doomsday =  FALSE ;
 
   MONITOR dining_philosophers ;   / / 모니터 선언 시작 
      CONST 
         Eating    =  0 ;    
         Hungry    =  1 ; 
         Thinking =  2 ; 
      VAR 
         I        :  INTEGER ;  / / 루프 변수 초기화 
         state    :  ARRAY  [ 0 .. 4 ]  OF  INTEGER ;  / / Eating, Hungry, Thinking 
         can_eat :  ARRAY  [ 0 .. 4 ]  OF CONDITION ;  / / 철학자 각각 대응 
         / / 젓가락이 모인까지 배고픈 철학자를 둔다
 
      PROCEDURE test ( k :  INTEGER ) ; 
      BEGIN 
         IF  ( state [ k ]  = Hungry ) 
            AND  ( state [ ( k + 4 )  MOD  5 ] <> Eating ) 
            AND  ( state [ ( k + 1 )  MOD  5 ] <> Eating )  THEN 
         BEGIN 
            state [ k ]  : = Eating ; 
            SIGNALC ( can_eat [ k ] ) ;  / / 대기 상태의 사람이 있으면 그 상태를 종료시키는 
         END ;  
      END ; 
 
      PROCEDURE pickup ( i :  INTEGER ) ; 
      BEGIN 
         state [ i ]  : = Hungry ; 
         WRITELN ( 'philosopher' , i , 'hungry' ) ; 
         test ( i ) ;  / / 옆 사람은 식사 중 또는? 
         IF state [ i ] <> Eating THEN 
            WAITC ( can_eat [ i ] ) ;   / / 이웃의 식사가 끝날 때까지 기다린다 
         WRITELN ( 'philosopher' , i , 'eating' ) ; 
      END ; 
 
      PROCEDURE putdown ( i :  INTEGER ) ; 
      BEGIN 
         state [ i ]  : = Thinking ; 
         WRITELN ( 'philosopher' , i , 'thinking' ) ; 
         test (  ( i + 4 )  MOD  5 ) ;  / / 왼쪽 이웃이 식사하는 기회 을주는 
         test (  ( i + 1 )  MOD  5 ) ;  / / 오른쪽 이웃이 식사하는 기회를 준다 
      END ;  
 
   BEGIN  / / 모니터를 초기화 
      FOR I : =  0  TO  4  DO state [ i ]  : = Thinking ; 
   END ;   / / 모니터 정의의 끝
 
   PROCEDURE philosopher ( i :  INTEGER ) ; 
   BEGIN 
      REPEAT 
         pickup ( i ) ;   / / 젓가락을 잡는 
         putdown ( i ) ;  / / 젓가락을 두는 
      UNTIL Doomsday ; 
   END ; 
 
BEGIN  / / 주 프로그램 
   COBEGIN   / / 5 개의 프로세스를 동시에 시작 
      philosopher ( 0 ) ; philosopher ( 1 ) ; philosopher ( 2 ) ; 
      philosopher ( 3 ) ; philosopher ( 4 ) ; 
   COEND ; 
END

다음 코드는 Java로 작성된 해법의 예이다 ( 세마포어 를 사용).

import  java.util.concurrent.Semaphore ; 
import  java.util.Random ; 
import  java.util.Vector ;
 
public  class philosopher extends  Thread 
{ 
    private  static  Final  Random rand =  New  Random ( ) ; 
    private  static  int event = 0 ; 
    private  static  String binary = "" ; 
    private  int ID ; 
    private Semaphore SEM ; 
    private  static Vector < Object [ ] > vector =  New Vector < Object [ ] > ( ) ;
 
    public philosopher ( int I, Semaphore s ) 
    { 
        id = i ; 
        sem = s ; 
        binary = binary +  "0" ; 
    }
 
    private  void busy ( ) 
    { 
        try 
        { 
            sleep ( rand. nextInt ( 1000 ) ) ; 
        }  catch  ( InterruptedException e ) { } 
    }
 
    private  void thinking ( ) 
    { 
        String str =  "Philosopher"  + id +  "is thinking" ; 
        vector. add (  this . addToObject ( System . currentTimeMillis ( ) , event, str )  ) ; 
        event + +; 
        busy ( ) ; 
    }
 
    private  void eating ( ) 
    { 
        String str = "Philosopher"  + id +  "is hungry and is trying to pick up his chopsticks" ; 
        vector. add (  this . addToObject ( System . currentTimeMillis ( ) , event, str )  ) ; 
        event + +; 
        busy ( ) ; 
        str =  "Philosopher"  + id +  "is eating" ; 
        this . oneToBinary ( id ) ; 
        vector. add (  this . addToObject ( System . currentTimeMillis ( ) , event, str )  ) ; 
        event + +; 
        busy ( ) ; 
        str =  "Philosopher"  + id +  "finished eating, and puts away his chopsticks" ; 
        this . zeroToBinary ( id ) ; 
        vector. add (  this . addToObject ( System . currentTimeMillis ( ) , event, str )  ) ; 
        event + +; 
    }
 
    private  Object [ ] addToObject ( long T, int I, String s ) { 
        Object [ ] o =  New  Object [ 4 ] ; 
        o [ 3 ]  = s ; 
        o [ 2 ]  = i ; 
        o [ 1 ]  = binary ; 
        o [ 0 ]  = t ; 
        return O ; 
    }
 
    private  void oneToBinary ( int I ) { 
        binary = binary. substring ( 0 , i )  +  "1"  + binary. substring ( i + 1 ) ; 
    }
 
    private  void zeroToBinary ( int I ) { 
        binary = binary. substring ( 0 , i )  +  "0"  + binary. substring ( i + 1 ) ; 
    }
 
    @ Override
    public  void run ( ) 
    { 
        for  ( int I =  0 ; i <  10 ;  + + i ) 
        { 
            thinking ( ) ; 
            try 
            { 
                sem. acquire ( ) ; 
            }  catch  ( InterruptedException e ) { } 
            eating ( ) ; 
            sem. release ( ) ; 
        } 
    }
 
    public  static  void Main ( String [ ] args ) 
    { 
        Final  int N =  5 ; 
        Semaphore sem =  New Semaphore ( N, true ) ; 
        Philosopher [ ] philosopher =  New philosopher [ N ] ;
 
        / / 철학자를 생성 
        for  ( int I =  0 ; i < N ; i + + )  { 
          philosopher [ i ]  =  New philosopher ( i, sem ) ; 
          philosopher [ i ] . start ( ) ; 
        } 
        / / 완료待ち合わせる
        for  ( int I =  0 ; i < N ; i + + )  { 
          try  { 
            philosopher [ i ] . join ( ) ; 
          }  catch ( InterruptedException EX )  { 
            return ; 
          } 
        }
 
        for  ( int I =  0 ; i < vector. size ( ) ; i + + )  { 
            Object [ ] o = vector. get ( i ) ; 
            System . out . printf ( "% d % d % s % s \ n " , o [ 0 ] , o [ 2 ] , o [ 1 ] , o [ 3 ] ) ; 
        } 
    } 
}

각주 편집 ]

  1. Dijkstra, Edsger W. EWD-1000. EW Dijkstra Archive. Center for American History, University of Texas at Austin. ( original ; transcription )
  2. J. Díaz; I. Ramos (1981). Formalization of Programming Concepts : International Colloquium, Peniscola, Spain, April 19-25, 1981. Proceedings . Birkhäuser. pp.  323 , 326 . ISBN  978-3-540-10699 - 9 .
  3. Hoare, CAR ( 2004 년 ). Communicating Sequential Processes " . usingcsp.com (originally published in 1985 by Prentice Hall International) 2012 년 7 월 16 일 보기.
  4. 해외에서 예
  5. Dijkstra, Edsger W. EWD-310. EW Dijkstra Archive. Center for American History, University of Texas at Austin. ( original ; transcription )
  6. Dijkstra, Edsger W. EWD-123. EW Dijkstra Archive. Center for American History, University of Texas at Austin. ( original ; transcription )

참고 문헌 편집 ]

  • Silberschatz, Abraham; Peterson, James L. (1988). Operating Systems Concepts . Addison-Wesley. ISBN  0-201-18760-4 .
  • Chandy, KM; Misra, J. (1984). The Drinking Philosophers Problem . ACM Transactions on Programming Languages ​​and Systems.
  • Dijkstra, EW (1971, June). Hierarchical ordering of sequential processes . Acta Informatica 1 (2) : 115-138.
  • Lehmann, DJ, Rabin M. O (1981) On the Advantages of Free Choice : A Symmetric and Fully Distributed Solution to the Dining Philosophers Problem. Principles Of Programming Languages ​​1981 ( POPL '81), pp. 133-138.

관련 항목 편집 ]

외부 링크 편집 ]




식사하는 철학자들 문제는 전산학에서 동시성과 교착 상태를 설명하는 예시로, 여러 프로세스가 동시에 돌아갈 때 교착 상태가 나타나는 원인을 직관적으로 알 수 있다.

다섯 명의 철학자가 원탁에 앉아 있고, 각자의 앞에는 스파게티가 있고 양옆에 젓가락이 하나씩이 있다. 그리고 각각의 철학자는 다른 철학자에게 말을 할 수 없다. 이때 철학자가 스파게티를 먹기 위해서는 양 옆의 젓가락을 동시에 들고 있어야 한다. 이때 각각의 철학자가 왼쪽의 젓가락을 들고 그 다음 오른쪽의 젓가락을 들어서 스파게티를 먹는 알고리즘을 가지고 있으면, 다섯 철학자가 동시에 왼쪽의 젓가락을 든 다음 오른쪽의 젓가락을 들 때까지 무한정 기다리는 교착 상태에 빠지게 될 수 있다.

또한 어떤 경우에는 동시에 두 젓가락을 집을 수 없어 식사를 하지 못하는 기아 상태가 발생할 수도 있고, 몇몇 철학자가 다른 철학자보다 식사를 적게 하는 경우가 발생하기도 한다.

[편집]해결책

에츠허르 데이크스트라의 해결책은 다음과 같다. 각각의 철학자를 P_1, P_2, P_3, P_4, P_5라고 하고, 각 철학자의 왼쪽 젓가락을 f_1, f_2, f_3, f_4, f_5라고 하자. P_5를 제외한 네 명은 먼저 f_n를 집은 후 f_{n+1}를 집는 방식을 취한다. 그리고 P_5는 이와 반대로, f_1를 먼저 집은 후 f_5를 집는다. 이것은 원래 방식의 대칭성을 제거하고, 따라서 교착 상태를 막을 수 있다.

[편집]같이 보기

[편집]바깥 고리



'System > Common' 카테고리의 다른 글

경영 정보학(Business Intelligence, BI)  (0) 2012.10.27
Lustre 파일시스템이란?  (0) 2012.10.27
awk  (0) 2012.08.30
UNIX 시스템에 필요한 10가지 핵심 도구  (0) 2012.08.23
캐리지 리턴(CR)과 라인 피드(LF), 뉴라인(/n)  (0) 2012.08.11
Posted by linuxism
,


01. 구글맵의 활용 – Google Maps API

구글맵의 활용 – Google Maps API

1). Google Maps API 제품군

Google Maps API는 사용자에게 무료로 제공되며 모든 웹사이트에서 사용 가능한 무료 서비스입니다. 사용료를 부과하거나 자산을 추적하거나 내부 애플리케이션을 구축하는 업체인 경우 향상된 기능, 기술 지원 및 서비스 수준 계약을 제공하는 Google Maps API Premier를 사용해야 합니다.

Google 지도에는 다양한 API가 있으며, 이를 통해 사용자는 자신의 웹사이트와 애플리케이션에 Google 지도의 강력한 기능과 일상적인 유용한 기능을 삽입하고 그 위에 자신의 데이터를 추가할 수 있습니다.

 

Maps JavaScript API
Javascript를 사용하여 웹페이지에 Google 지도를 삽입합니다. 지도를 조작하고 다양한 서비스를 통해 콘텐츠를 추가합니다.
Maps JavaScript API 바로가기 
Maps API for Flash
Flash 기반 웹페이지나 애플리케이션에 Google 지도를 삽입하려면 이 ActionScript API를 사용합니다. 3차원으로 지도를 조작하고 다양한 서비스를 통해 콘텐츠를 추가합니다.Maps API for Flash 바로가기 
 Google Earth API진정한 3D 디지털 지구를 웹페이지에 삽입합니다. 웹페이지를 벗어나지 않은 채 방문자를 지구 어느 곳으로든(심지어 바다 속으로도) 안내할 수 있습니다.Google Earth API 바로가기 
Static Maps API빠르고 간단하게 Google 지도 이미지를 자신의 웹페이지나 모바일 사이트에 삽입합니다. Javascript나 동적 페이지 로딩은 필요하지 않습니다.Static Maps API 바로가기 
웹 서비스URL 요청을 사용하여 클라이언트 애플리케이션에서 지오코딩, 길찾기, 고도 및 장소 정보에 접근하고 결과를 JSON이나 XML로 조작합니다.웹 서비스 바로가기 
지도 데이터 API지형지물의 모델(Placemark, 선 및 모양)과 지형지물 모음을 사용하여 Google Data API 피드를 통해 지도 데이터를 보고 저장하고 업데이트합니다. (현재 서비스 되지 않음)지도 데이터 API 바로가기 

2). Google Map Javascript API V3 자주있는 질문(FaQ) 정리

Q. 휴대기기에 지도 애플리케이션을 전달하려면 어떻게 해야 하나요?

A. Google 지도 자바스크립트 API V3은 모바일 기기에 맞게 개발되었으며, Apple iPhone같이 전체 자바스크립트가 구현된 웹브라우저를 포함하는 기기 및 데스크톱을 대상으로 하는 브라우저 애플리케이션에 적합합니다.

자바스크립트 API를 사용할 수 없는 기기를 대상으로 하는 브라우저 기반 애플리케이션의 경우에는 정적 지도 API에서 마커 및 폴리라인이 포함된 GIF, JPG 및 PNG 형식의 지도 이미지를 제공합니다. 브라우저 외부에서 정적 지도 API를 사용하려면 지도 이미지를 Google 지도에 연결해야 합니다.

Android 애플리케이션에서 지도를 통합하려면 Android 지도 외부 라이브러리를 사용합니다.
기본 iPhone 애플리케이션에 지도를 통합하려면 Apple iPhone MapKit 프레임워크를 사용합니다.

Q. Google 지도 JS API가 지원하는 웹 브라우저는 무엇인가요?

A. Google Maps JavaScript API는 다음의 웹브라우저를 지원합니다.

Google 지도 자바스크립트 API V3:

• IE 7.0+ (Windows)

• Firefox 3.0 이상 (Windows, Mac OS X, Linux)

• Safari 4+(Mac OS X, iOS)

• Chrome(Windows, Mac OS X, Linux)

• Android

• BlackBerry 6

• Dolfin 2.0+(삼성 바다)

 

Q. 상업용 웹사이트에서 Google Maps API를 사용할 수 있나요?

A. 일반적으로 사이트에 사용자가 무료로 접근할 수 있다면 Google Maps API를 사용할 수 있습니다. 예를 들어, 광고를 통해 유지되는 웹사이트는 Google 지도 API 이용약관에 부합할 가능성이 큽니다. 지도에 정보를 올리는 사람(부동산 매물 정보 등록 등)에게 비용을 받더라도 해당 정보를 Google 지도 API를 사용하여 사이트의 무료 영역에 표시한다면 Google 지도 API 이용약관에 부합합니다.

하지만 모든 상업용 사용이 허용되는 것은 아닙니다. 예를 들어 다음 기준 중에서 하나라도 해당되는 내용이 있는 사이트는 알맞은 Google 지도 API 고급형 라이센스를 구입해야 합니다.

• 유료 고객만 사이트를 사용할 수 있는 경우.

• 회사나 인트라넷 안에서만 사이트에 액세스할 수 있는 경우.

• 업체 발송, 차량 관리 또는 비즈니스 자산 추적과 관련된 애플리케이션 또는 이와 유사한 애플리케이션

Google은 Google 지도 API 사용을 언제든지 일시 중지하거나 종료할 수 있는 권리를 가지고 있습니다. 이용약관을 자세히 읽어주세요.

[자료출처: Google Map API, http://code.google.com/intl/ko-KR/apis/maps/]


출처 - http://itta.co.kr/?p=243



Posted by linuxism
,


1.1 배열(Array)이란?

같은 타입의 여러 변수를 하나의 묶음으로 다루는 것을 "배열"이라고 한다. 많은 양의 데이터를 저장하기 위해서, 그 데이터의 숫자만큼 변수를 선언해야 한다면 매우 혼란스러울 것이다. 
이런 경우에 배열을 사용하면 하나의 변수로 많은 양의 데이터를 손쉽게 다룰 수 있다. 

[참고]서로 다른 타입의 데이터를 하나로 묶어서 다루려면, 클래스를 정의해서 사용하면 된다. 

한 학급의 시험점수를 저장하고자 할 때가 배열을 사용하기 좋은 예이다. 만일 배열을 사용하지 않는다면 5명의 학생의 점수를 저장하기 위해서 아래와 같이 해야 할 것이다. 


int score1=0, score2=0, score3=0, score4=0, score5=0 ; 


하지만, 배열을 사용하면 다음과 같이 간단히 할 수 있다. 


int[] score = new int[5]; // 5개의 int 값을 저장할 수 있는 배열을 생성한다. 


 
[그림5-1] 메모리에 생성된 변수들과 배열 


1.2 배열의 선언

배열을 선언하는 방법은 간단하다. 원하는 타입의 변수를 선언하고 변수 또는 타입에 배열임을 의미하는 대괄호[]를 붙이면 된다. 





1.3 배열의 생성

배열을 선언한 다음에는 배열을 생성해야한다. 배열을 선언하는 것은 단지 생성된 배열을 다루기 위한 참조변수를 위한 공간이 만들어지는 것 뿐이다. 배열을 생성해야만 비로소 데이터를 저장할 수 있는 공간이 만들어지는 것이다. 

배열을 생성하기 위해서는 new연산자를 사용하고 배열의 타입과 크기를 지정해 주어야 한다. 



int[] score;  // 배열을 선언한다.(생성된 배열을 다루는데 사용될 참조변수 선언) 
score = new int[5];  // 배열을 생성한다.(5개의 int값을 저장할 수 있는 공간생성) 


[참고] 위의 두 문장은int[] score = new int[5];와 같이 한 문장으로 줄여 쓸 수 있다. 

사실 배열도 객체이기 때문에 멤버변수와 메서드를 갖고 있으며, 이 중 멤버변수 length는 배열의 크기에 대한 정보를 담고 있다. 위의 예제코드에서 score의 크기가 5이므로 score.length의 값은 5가 된다. 
[참고] 배열은 한번 생성되면 크기를 변경할 수 없다. 

배열의 선언과 생성과정을 단계별로 그림과 함께 자세히 살펴보도록 하자. 

1. int[] score; 
int형 배열 참조변수 score를 선언한다. 데이터를 저장할 수 있는 공간은 아직 마련되지 않았다. 

 

2. score = new int[5]; 
new연산자에 의해서 메모리의 빈공간에 5개의 int형 데이터를 저장할 수 있는 공간이 마련된다. 

 

그리고 각 배열요소는 자동적으로 int의 기본값(default)인 0으로 초기화 된다. 



마지막으로 할당연산자(=)에 의해서 배열의 주소가 int형 배열 참조변수 score에 저장된다. 

 
[참고]배열이 주소 0x100번지에 생성되었다고 가정한 것이다. 


이번엔 참조변수 배열의 예를 들어보자. 



String[] name;             // String타입의 참조변수 배열을 선언한다. 
name = new String[3];       // String인스턴스의 참조변수를 담을 수 있는 배열을 생성한다. 



위의 두 문장을 수행한 결과를 그림으로 표현하면 다음과 같다. 3개의 String타입의 참조변수를 저장하기 위한 공간이 마련되고 참조변수의 기본값은 null이므로 각 배열요소의 값은 null로 초기화 된다. 

 <BR> 

참고로 변수의 타입에 따른 기본값은 다음과 같다. 

 
[표5-1] 타입에 따른 변수의 기본값 



1.4 배열의 초기화

배열은 생성과 동시에 자동적으로 자신의 타입에 해당하는 기본값으로 초기화되므로 배열을 사용하기 전에 초기화를 해주지 않아도 되지만, 원하는 값으로 초기화 하기 위해서 다음과 같이 한다. 


int[] score = new int[5];      // 크기가 5인 int형 배열을 생성한다. 
score[0] = 100;                   // 각 요소에 직접 값을 저장한다. 
score[1] = 90; 
score[2] = 80; 
score[3] = 70; 
score[4] = 60; 


 

String[] name = new String[3]; 
name[0] = new String("Kim"); 
name[1] = new String("Park"); 
name[2] = new String("Yi"); 


 <BR> 

이처럼 배열의 각 요소를 하나씩 초기화하는 것은 좀 불편하다. 그래서 자바에서는 보다 간편한 초기화 방법들을 제공한다. 이 때 배열의 크기는 따로 지정해주지 않으며 주어진 값의 개수에 따라 크기가 결정된다. 



int[] score = { 100, 90, 80, 70, 60}; 
int[] score = new int[]{ 100, 90, 80, 70, 60}; 

String[] name = { new String("Kim"), new String("Park"), new String("Yi")}; 
String[] name = { "Kim""Park""Yi"}; 
String[] name = new String[]{ new String("Kim"), new String("Park"), new String("Yi")}; 


[참고]String은 클래스이므로 new연산자를 통해 인스턴스를 생성해야하지만, "Kim"과 같이 쌍따옴표를 사용해서 간략히 표현하는 것을 특별히 허용한다. 



1.4 배열의 활용

배열의 각 저장공간에 값을 저장하고 또는 저장된 값을 읽어오기 위해서는 배열 참조변수와 인덱스를 이용한다. 배열의 인덱스는 배열의 각 저장공간에 자동적으로 주어지는 일련 번호인데, 0부터 시작해서 1씩 증가하는 연속적인 값이다. 크기가 5인 배열에서는 index의 범위가 0~4까지 모두 5개가 된다. 
배열의 값을 읽거나 저장하기 위해서는 다음과 같이 배열 참조변수와 배열의 인덱스를 사용하면 된다. 


score[3] = 100;         // 배열 score의 4번째 요소에 100을 저장한다. 
int value = score[3]; // 배열 score의 4번째 요소에 저장된 값을 읽어서 value에 저장한다. 


배열을 다루는데 있어서 for문은 거의 필수적으로 사용된다. 이 때 for문의 조건식으로 배열의 크기를 
직접 적어주는 것보다 배열의 속성인 length를 사용하는 것이 더 견고한 코드를 만든다. 


int[] score = { 100, 90, 80, 70, 60, 50 }; 

for (int i=0; i < 6; i++) { 
          System.out.println(score[i]); 



위의 코드는 배열의 각 요소를 for문을 이용해서 출력하는 일을 한다. 여기서 score배열의 크기는 6이며 인덱스의 범위는 0~5이다. 
이 때 코드를 다음과 같이 변경하여 배열에 저장될 값을 하나 줄인다면, 배열의 크기가 5로 변경되었으므로 유효한 인덱스의 범위는 0~4가 된다. 


int[] score = { 100, 90, 80, 70, 60 }; 

for (int i=0; i < 6; i++) { 
          System.out.println(score[i]); 



배열의 크기가 변경되었으니 for문에 사용되는 조건의 범위도 변경해주어야 하는데, 만일 이 것을 잊고 실행한다면 for문은 배열의 유효한 인덱스 범위인 0~4를 넘어 0부터 5까지 반복하기 때문에 5번째 반복에서 ArrayIndexOutOfBoundsException이라는 예외(일종의 에러)가 발생하여 비정상적으로 종료될 것이다. 

그래서 이러한 경우에는 for문의 조건식에 배열의 크기를 직접 적어주는 것보다 배열의 멤버변수인 length를 사용하는 것이 좋다. 위의 for문을 length를 사용해서 변경하면 다음과 같다. 


for(int i=0; i < score.length; i++) { 
     System.out.println(score[i]); 



length는 배열의 크기가 변경됨에 따라 자동적으로 변경된 배열의 크기를 갖기 때문에, 배열의 처리에 사용되는 for문의 조건식을 일일이 변경해주지 않아도 된다. 
이처럼 배열의 크기를 직접 적어주는 것보다 배열의 멤버변수인 length를 사용하는 것이 더 편리하고 안전하다. 

[예제5-1] ArrayEx1.java

class ArrayEx1 

      public static void main(String[] args) 
      { 
            int sum =0;                         // 총점을 저장하기 위한 변수 
            float average = 0f;             // 평균을 저장하기 위한 변수 

            int[] score = {100, 88, 100, 100, 90}; 

            for (int i=0; i < score.length ; i++ ) { 
                  sum += score[i];       // 반복문을 이용해서 배열에 저장되어 있는 값들을 더한다. 
            } 
            average = sum / (float)score.length ; // 계산결과를 float로 얻기 위함. 

            System.out.println("총점 : " + sum); 
            System.out.println("평균 : " + average); 
      } 

[실행결과]
총점 : 478 
평균 : 95.6 

for문을 이용해서 배열에 저장된 값을 모두 더한 결과를 배열의 개수로 나누어서 평균을 구하는 예제이다. 평균을 구하기 위해 전체 합을 배열의 크기인 score.length로 나누었다.
이 때 int와 int간의 연산은 int를 결과로 얻기 때문에 정확한 평균값을 얻지 못하므로 score.length를 float로 변환하여 나눗셈을 하였다. 

[예제5-2] ArrayEx2.java

class ArrayEx2 

      public static void main(String[] args) 
      { 
            int[] number = new int[10]; 

            for (int i=0; i < number.length ; i++ ) { 
                  System.out.print(number[i] = i);       // 배열을 0부터 9의 숫자로 초기화한다. 
            } 
            System.out.println(); 

            for (int i=0; i < 100; i++ ) { 
                  int n = (int)(Math.random() * 10);       // 0~9중의 한 값을 임의로 얻는다. 
                  int temp = number[0]; // 배열의 첫 번째 값과 임의로 선택된 위치의 값과 바꾼다. 
                  number[0] = number[n]; 
                  number[n] = temp; 
            } 
            for (int i=0; i < number.length ; i++ ) { 
                  System.out.print(number[i]);             // 배열의 내용을 출력한다. 
            } 
      } 

[실행결과]
0123456789 
5827164930 

크기가 10인 배열을 생성하고 0~9의 숫자로 차례대로 초기화하여 출력한다. 그 다음 random메서드를 이용해서 배열의 임의의 위치에 있는 값과 배열의 첫 번째 값을 교환하는 일을 100번 반복한다. 
그리고 그 결과를 화면에 출력한다. 이 예제를 응용하면 카드게임에서 카드를 한 벌을 생성하여 초기화한 다음 카드를 섞는 것과 같은 일을 할 수 있을 것이다. 
[참고]random메서드를 이용했기 때문에 실행 할 때 마다 결과가 다를 수 있다. 

[예제5-3] ArrayEx3.java


class ArrayEx3 

      public static void main(String[] args) 
      { 
            int[] number = new int[10]; 

            for (int i=0; i < number.length ; i++ ) { 
                  System.out.print(number[i] = (int)(Math.random() * 10)); 
            } 
            System.out.println(); 

            for (int i=0; i < number.length ; i++ ) { 
                  boolean changed = false;       // 자리바꿈이 발생했는지를 체크한다. 
                  for (int j=0; j < number.length-1-i ; j++ ) { 
                        if(number[j] > number[j+1]) { // 옆의 값이 크면 서로 바꾼다. 
                              int temp = number[j]; 
                              number[j] = number[j+1]; 
                              number[j+1] = temp; 
                              changed = true;       // 자리바꿈이 발생했으므로 changed를 true로. 
                        } // end if 
                  } // end for j 
                  for(int k=0; k < number.length; k++)

                        System.out.print(number[k]);       // 매 반복마다 정렬된 결과를 출력한다. 
                  System.out.println(); 
                  if (!changed) break;       // 자리바꿈이 없으면 반복문을 벗어난다. 
            } // end for i 
      } 

[실행결과]
1344213843 
1342134438 
1321344348 
1213343448 
1123334448 
1123334448 

크기가 10인 배열에 0과 9사이의 임의의 값으로 채운다음, 버블정렬 알고리즘을 통해서 크기순으로 정렬하는 예제이다. 이 알고리즘의 정렬방법은 아주 간단하다. 배열의 크기가 n일 때, 배열의 첫 번째부터 n-1까지의 요소에 대해, 근접한 값과 크기를 비교하여 자리바꿈을 반복하는 것이다. 
보다 효율적인 작업을 위해 changed라는 boolean형 변수를 두어서 자리바꿈이 없으면 break문을 수행하여 정렬을 마치도록 했다. 자리바꿈이 없다는 것은 정렬이 완료되었음을 뜻하기 때문이다. 
버블정렬은 다소 비효율적이긴 하지만 가장 간단한 정렬방법이다. 

[예제5-4] ArrayEx4.java

class ArrayEx4 

      public static void main(String[] args) 
      { 
            int[] number = new int[10]; 
            int[] counter = new int[10]; 

            for (int i=0; i < number.length ; i++ ) { 
                  System.out.print(number[i] = (int)(Math.random() * 10)); 
            } 
            System.out.println(); 

            for (int i=0; i < number.length ; i++ ) { 
                  counter[number[i]]++; 
            } 

            for (int i=0; i < number.length ; i++ ) { 
                  System.out.println( i +"의 개수 :"+ counter[i]); 
            } 
            
      } 

[실행결과]
4446579753 
0의 개수 :0 
1의 개수 :0 
2의 개수 :0 
3의 개수 :1 
4의 개수 :3 
5의 개수 :2 
6의 개수 :1 
7의 개수 :2 
8의 개수 :0 
9의 개수 :1 

이전 예제에서와 같이 크기가 10인 배열을 만들고 0과 9사이의 임의의 값으로 초기화 한다. 그리고 이 배열에 저장된 각 숫자가 몇번 반복해서 나타나는지를 세어서 배열 counter에 담은 다음 화면에 출력한다. 
[플래시동영상]자료실의 Array.swf을 보면 예제를 설명과 함께 순차적으로 실행과정을 볼수 있다.

[예제5-5] ArrayEx5.java

class ArrayEx5 

      public static void main(String[] args) 
      { 
            char[] hex = { 'C', 'A', 'F', 'E'}; 

            String[] binary = {"0000""0001""0010""0011" 
                                    , "0100""0101""0110""0111" 
                                    , "1000""1001""1010""1011" 
                                    , "1100""1101""1110""1111" }; 

            String result=""

            for (int i=0; i < hex.length ; i++ ) {             
                  if(hex[i] >='0' && hex[i] <='9') { 
                        result +=binary[hex[i]-'0'];       // '8'-'0'의 결과는 8이다. 
                  } else {       // A~F이면 
                        result +=binary[hex[i]-'A'+10]; // 'C'-'A'의 결과는 2 
                  } 
            } 
            System.out.println("hex:"new String(hex)); 
            System.out.println("binary:"+result); 
      } 

[실행결과]
hex:CAFE 
binary:1100101011111110 

16진수를 2진수로 변환하는 예제이다. 먼저 변환하고자 하는 16진수를 배열 hex에 나열한다. 16진수에는 A~F까지 6개의 문자가 포함되므로 char배열로 처리하였다. 그리고 String배열 binary는 이진수 0000 부터 1111(16진수로 0~F)까지 모두 16개의 값을 String으로 저장하였다. 
for문을 이용해서 배열 hex에 저장된 문자를 하나씩 읽어서 그에 해당하는 이진수 표현을 배열 binary에서 얻어 result에 덧붙이고 그 결과를 화면에 출력한다. 
[참고]16진수 1자리는 2진수 4자리에 해당된다. 16진수 A는 십진수로 10이고 B는 11이다. 

[예제5-6] ArrayEx6.java

class ArrayEx6 

      public static void main(String[] args) 
      { 
            String source = "SOSHELP"
            String[] morse = {".-""-...""-.-.","-..""." 
                                    ,"..-.""--.""....","..",".---" 
                                    , "-.-"".-..""--""-.""---" 
                                    , ".--.""--.-",".-.","...","-" 
                                    , "..-""...-"".--""-..-" 
                                    ,"-.--""--.." }; 
            String result=""

            for (int i=0; i < source.length() ; i++ ) { 
                  result+=morse[source.charAt(i)-'A']; 
            } 
            System.out.println("source:"+ source); 
            System.out.println("morse:"+result); 
      } 

[실행결과]
source:SOSHELP 
morse:...---.........-...--. 

문자열(String)을 모르스(morse)부호로 변환하는 예제이다. 이전의 16진수를 2진수로 변환하는 예제와 같지만, char배열 대신 이번엔 String을 사용했다. 
String의 문자의 개수는 length()를 통해서 얻을 수 있고, charAt(int i)메서드는 String의 i번째 문자를 반환한다. 그래서 for문의 조건식에 length()를 사용하고 charAt(int i)메서드를 통해서 source에서 한 문자씩 차례대로 읽어 올 수 있다. 

[참고]String클래스는 char배열을 내부 데이터로 갖으며 char배열을 다루는데 필요한 다양한 메서드를 제공한다. 




1.4 다차원 배열

자바에서는 1차원 배열 뿐만 아니라 2차원 이상의 다차원 배열도 허용한다. 그러나 특별한 경우를 제외하고는 2차원 이상의 배열은 잘 사용되지 않는다. 
그리고 2차원 배열을 잘 이해하면 2차원 이상의 배열에 응용하는 것은 그리 어렵지 않다. 본서에서는 2차원 배열에 대해서만 설명하도록 하겠다. 우선 2차원 배열의 선언방법은 다음과 같다. 

 

[참고]3차원이상의 고차원 배열의 선언은 대괄호[]의 개수를 차원 수 만큼 추가해 주기만 하면 된다. 

2차원 배열은 주로 테이블 형태의 데이터를 담는데 사용되며, 만일 5행 3열의 데이터를 담기 위한 배열을 생성하려면 다음과 같이한다. 


int[][] score = new int[5][3];      // 5행 3열의 2차원 배열을 생성한다. 


위 문장이 수행되면 score[0][0]부터 score[4][2]까지 15개의 저장공간이 마련된다. 

 

위와 같은 테이블형태의 데이터를 배열에 저장하기 위해서는 다음과 같이 한다. 


score[0][0]=100 ; 
score[0][1]=100 ; 
score[0][2]=100 ; 
score[1][0]=20 ; 
score[1][1]=20 ; 
... 
score[4][2]=50 ; 


1차원 배열에서와 같이 중괄호{}를 이용해서 2차원 배열의 생성과 초기화를 동시에 할 수도 있다. 


int[][] score = {{100, 100, 100}, {20, 20, 20}, {30, 30, 30}, {40, 40, 40}, {50, 50, 50}}; 


 

5행 3열의 2차원 배열을 생성하고 초기화한 결과를 그림으로 나타낸 것이다. 그림에서 알 수 있듯이 2차원 배열은 "배열의 배열"로 구성되어 있음을 알 수 있다. 즉, 여러 개의 배열이 모여서 또 하나의 배열을 이루고 있는 것이다. 
여기서 score.length의 값은 얼마일까? 배열 참조변수 score가 참조하고 있는 배열의 크기가 얼마인가를 세어보면 될 것이다. 그래서 score.length의 값은 5이다. score[0].length은 배열 참조변수 score[0]이 참조하고 있는 배열의 크기이므로 3이고 score[1].length, score[2].length, score[3].length, score[4].length의 값 역시 모두 3이라는 것을 쉽게 알 수 있을 것이다. 
만일 for문을 이용해서 2차원 배열을 초기화 한다면 다음과 같을 것이다. 


for (int i=0; i < score.length; i++) { 
     for (int j=0; j < score[i].length; j++) { 
           score[i][j] = 10; 
     } 




위의 코드는 2차원 배열 score의 모든 요소를 10으로 초기화 한다. 

[예제5-7] ArrayEx7.java

class ArrayEx7 

      public static void main(String[] args) 
      { 
            int[][] score = {{ 100, 100, 100} 
                                    , { 20, 20, 20} 
                                    , { 30, 30, 30} 
                                    , { 40, 40, 40} 
                                    , { 50, 50, 50}}; 
            int koreanTotal = 0; 
            int englishTotal = 0; 
            int mathTotal = 0; 

      System.out.println("번호 국어 영어 수학 총점 평균 "); 
      System.out.println("============================="); 

            for(int i=0;i < score.length;i++) { 
                  int sum=0; 
                  koreanTotal += score[i][0]; 
                  englishTotal += score[i][1]; 
                  mathTotal += score[i][2]; 
                  System.out.print(" " + (i + 1) + " "); 
                  for(int j=0;j < score[i].length;j++) { 
                        sum+=score[i][j]; 
                        System.out.print(score[i][j]+" "); 
                  } 
                  System.out.println(sum + " " + sum/(float)score[i].length); 
            } 
      System.out.println("============================="); 
      System.out.println("총점:" + koreanTotal + " " +englishTotal +" " +mathTotal); 
      } 

[실행결과]
번호 국어 영어 수학 총점 평균 
============================= 
1 100 100 100 300 100.0 
2 20 20 20 60 20.0 
3 30 30 30 90 30.0 
4 40 40 40 120 40.0 
5 50 50 50 150 50.0 
============================= 
총점:240 240 240 

5명의 학생의 세 과목 점수를 더해서 각 학생의 총점과 평균을 계산하고 과목별 총점을 계산하는 예제이다. 




1.7 가변 배열

자바에서는 2차원 이상의 배열에 대해서 "배열의 배열"의 형태로 처리한다는 사실을 이용하면 보다 자유로운 형태의 배열을 구성할 수 있다. 
2차원 이상의 다차원 배열을 생성할 때 전체 배열 차수 중 마지막 차수의 크기를 지정하지 않고, 추후에 각기 다른 크기의 배열을 생성함으로써 고정된 형태가 아닌 보다 유동적인 가변 배열을 구성할 수 있다. 
만일 다음과 같이 5 * 3크기의 2차원 배열 score를 생성하는 코드가 있을 때, 



int[][] score = new int[5][3]; 


위 코드를 다음과 같이 표현 할 수 있다. 



int[][] score = new int[5][];       // 두 번째 차원의 크기는 지정하지 않는다. 
score[0] = new int[3]; 
score[1] = new int[3]; 
score[2] = new int[3]; 
score[3] = new int[3]; 
score[4] = new int[3]; 


첫 번째 코드와 같이 2차원 배열을 생성하면 직사각형 테이블 형태의 고정적인 배열만 생성할 수 있지만, 두 번째 코드와 같이 2차원 배열을 생성하면 다음과 같이 각 열마다 다른 크기의 배열이 생성하는 것이 가능하다. 


int[][] score = new int[5][]; 
score[0] = new int[4]; 
score[1] = new int[3]; 
score[2] = new int[2]; 
score[3] = new int[2]; 
score[4] = new int[3]; 


위의 코드에 의해서 생성된 2차원 배열을 그림으로 표현하면 다음과 같다. 

 

score.length의 값은 여전히 5지만, 전과는 달리 score[0].length의 값은 4이고 score[1].length의 값은 3으로 서로 다르다. 
가변배열 역시 중괄호{}를 이용해서 다음과 같이 생성과 초기화를 동시에 하는 것이 가능하다. 


int[][] score = {{100, 100, 100, 100}, {20, 20, 20}, {30, 30}, {40, 40}, {50, 50, 50}}; 


[플래시동영상]자료실의 MultiDim.swf을 보면 예제를 설명과 함께 순차적으로 실행과정을 볼수 있다.



1.8 배열의 복사

배열은 한번 생성하면 그 크기를 변경할 수 없기 때문에 더 많은 저장공간이 필요하다면 보다 큰 배열을 새로 만들고 이전 배열로부터 내용을 복사해야한다. 
배열 간의 내용을 복사하려면 for문을 사용하거나 System클래스의 arraycopy메서드를 사용하면 된다. 예제를 통해서 배열을 복사하는 방법에 대해서 알아보도록 하자. 

[예제5-8] ArrayEx8.java

class ArrayEx8 

      public static void main(String[] args) 
      { 
            int[] number = {0,1,2,3,4,5}; 
            int[] newNumber = new int[10]; 
            
            for(int i=0; i < number.length;i++) { 
                  // 배열 number의 값을 newNumber에 저장한다. 
                  newNumber[i] = number[i];
            } 

            for(int i=0;i < newNumber.length;i++) { 
                  System.out.print(newNumber[i]); 
            } 
      } 

[실행결과]
0123450000 

더 큰 크기의 새로운 배열을 새로 만든 다음 이전 배열의 내용을 for문을 사용해서 복사하는 예제이다. 배열은 생성과 동시에 자동적으로 자신의 타입의 기본값으로 초기화 되므로 배열 newNumber의 요소들이 int의 기본값인 0으로 초기화 되었다는 것을 알 수 있다. 

System클래스의 arraycopy메서드를 사용하면 보다 간단히 배열을 복사할 수 있다. arraycopy메서드는 배열에 저장되어 있는 값만을 복사하기 때문에 참조변수 배열인 경우에는 단지 주소값만을 복사할 뿐 참조변수가 가리키고 있는 객체를 복사하지는 않는다. 
배열 abc와 number가 존재한다고 가정하고, abc의 내용을 number로 모두 복사하려면 다음과 같이 하면 된다. 


System.arraycopy(abc, 0, number, 0, abc.length);       


배열 abc의 내용을 배열 number로, 배열 abc에서 인덱스 0의 위치부터 시작해서 abc.length 만큼을 number의 인덱스 0인 위치에 복사한다. 
이때 복사하려는 배열의 위치가 적절하지 못하여 복사하려는 내용보다 여유공간이 적으면 ArrayIndexOutOfBoundsException이 발생한다. 

[예제5-9] ArrayEx9.java

class ArrayEx9 

      public static void main(String[] args) 
      { 
            char[] abc = { 'A', 'B', 'C', 'D'}; 
            char[] number = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; 
            System.out.println(new String(abc)); 
            System.out.println(new String(number)); 

            // 배열 abc와 number를 붙여서 하나의 배열(result)로 만든다. 
            char[] result = new char[abc.length+number.length]; 
            System.arraycopy(abc, 0, result, 0, abc.length); 
            System.arraycopy(number, 0, result, abc.length, number.length); 
            System.out.println(new String(result)); 

            // 배열 abc을 배열 number의 첫 번째 위치부터 배열 abc의 크기만큼 복사 
            System.arraycopy(abc, 0, number, 0, abc.length);       
            System.out.println(new String(number)); 
            System.arraycopy(abc, 0, number, 6, 3);       // number의 인덱스6 위치에 3개를 복사 
            System.out.println(new String(number)); 
      } 

[실행결과]
ABCD 
0123456789 
ABCD0123456789 
ABCD456789 
ABCD45ABC9 




1.9 커맨드라인을 통해 입력받기

System.in.read()이외에 화면을 통해 사용자로부터 값을 입력받을 수 있는 간단한 방법이 있다. 바로 커맨드라인을 이용한 방법인데, 프로그램을 실행할 때 클래스이름 뒤에 공백문자로 구분하여 여러 개의 문자열을 프로그램에 전달 할 수 있다. 
만일 실행할 프로그램의 main메서드가 담긴 클래스의 이름이 MainTest라고 가정하면 다음과 같이 실행할 수 있을 것이다. 


c:\j2sdk1.4.1\work>java MainTest abc 123 

 

커맨드라인을 통해 입력된 두 문자열은 String 배열에 담겨서 MainTest클래스의 main메서드의 매개변수(args)에 전달된다. 그리고는 main메서드 내에서 args[0],args[1]과 같은 방식으로 커맨드라인으로 부터 전달받은 문자열에 접근할 수 있다. 여기서 args[0]는 "abc"이고 args[1]은 "123"이다.


 

[예제5-10] MainTest.java


class MainTest 

      public static void main(String[] args) 
      { 
            System.out.println("매개변수의 개수:"+args.length); 
            for(int i=0;i< args.length;i++) { 
                  System.out.println("args[" + i + "] = \""+ args[i] + "\"");

            } 

      } 

[실행결과]
C:\j2sdk1.4.1\work>java MainTest abc 123 "John Lim" 
매개변수의 개수:3 
args[0] = "abc" 
args[1] = "123" 
args[2] = "John Lim" 



커맨드라인에 입력된 매개변수는 공백문자로 구분하기 때문에 입력될 매개변수값에 공백이 있는 경우 겹따옴표(")로 감싸 주어야 한다. 그리고 커맨드라인에서 숫자를 입력해도 숫자가 아닌 문자열로 처리된다는 것에 주의해야한다. 

[예제5-11] MorseConverter.java

class MorseConverter 

      public static void main(String[] args) 
      { 
            if (args.length !=1) { 
                  System.out.println("usage: java MorseConverter WORD"); 
                  System.exit(0); 
            } 

            System.out.println("source:"+ args[0]); 
            String source = args[0].toUpperCase(); // 대문자로 변환한다. 
            
            String[] morse = {".-""-...""-.-.","-..""." 
                                    ,"..-.""--.""....","..",".---" 
                                    , "-.-"".-..""--""-.""---" 
                                    , ".--.""--.-",".-.","...","-" 
                                    , "..-""...-"".--""-..-" 
                                    ,"-.--""--.." }; 
            String result=""

            for (int i=0; i < source.length() ; i++ ) { 
                  result+=morse[source.charAt(i)-'A']; 
            } 
            
            System.out.println("morse:"+result); 
      } 

[실행결과]
C:\j2sdk1.4.1\work>java MorseConverter 
usage: java MorseConverter WORD 

C:\j2sdk1.4.1\work>java MorseConverter sos 
source:sos 
morse:...---... 

예제5-6을 변경하여 모르스부호로 변환할 문자열을 커맨드라인으로부터 입력받도록 변경하였다. 대문자만 변경 가능하도록 작성되어 있기 때문에 String클래스의 toUpperCase()를 이용해서 대문자로 변경한 다음에 모르스부호로 변환한다. 
그리고 실행 시 매개변수를 입력하지 않거나 둘 이상 입력하게 되면 사용법을 출력한다. 

[참고]커맨드라인에 매개변수를 입력하지 않으면 크기가 0인 배열이 생성되어 args.length의 값은 0이 된다. 이처럼 크기가 0인 배열을 생성하는 것도 가능하다.


출처 - http://cafe.naver.com/javachobostudy





'Development > JavaEssential' 카테고리의 다른 글

JRE (Java runtime environment)  (0) 2012.11.07
java - override  (0) 2012.11.03
java - 연산자 2  (1) 2012.10.07
java - 반복문  (0) 2012.10.03
java - 변수  (0) 2012.09.24
Posted by linuxism
,