2009-05-15 2 views
7

교착 상태에 대한 아래 midlet 코드 (클래스 Moo)의 간단한 부분 (최소한 스레드 here에서이 게시물을 읽은 후 교착 상태로 가정).j2me 네트워킹, 스레드 및 교착 상태

나는 게시물에서 관련 발췌를 재현 : 문제의

 

    String url = ... 
    Connection conn = null; 

    try { 
     conn = Connector.open(url); 
     // do something here 
    } 
    catch(IOException e){ 
     // error 
    } 
 

뿌리는 오픈() 호출의 차단 특성이다. 일부 플랫폼에서는 시스템이 별도의 스레드에 해당하는 실제 연결을 덮개 아래에서 수행합니다. 호출 스레드는 연결 스레드가 연결을 만들 때까지 차단됩니다. 동시에 보안 하위 시스템에서는 사용자가 연결을 확인하도록 요구할 수 있으며 연결 스레드는 이벤트 스레드가 사용자로부터 확인을받을 때까지 차단됩니다. 교착 상태는 이벤트 스레드가 이미 연결 스레드를 기다리고 있기 때문에 발생합니다. 시스템 스레드 호출이 여기 다 (이벤트 및 알림 스레드)와 교착 상태에 이르는 일련의 이벤트 방법

 

public class Moo extends MIDlet { 

    protected void destroyApp(boolean arg0) throws MIDletStateChangeException { 
     // TODO Auto-generated method stub 

    } 

    protected void pauseApp() { 
    } 

    protected void startApp() throws MIDletStateChangeException { 
     Display display = Display.getDisplay(this); 
     MyCanvas myCanvas = new MyCanvas(); 
     display.setCurrent(myCanvas); 
     myCanvas.repaint(); 

    } 

    class MyCanvas extends Canvas { 

     protected void paint(Graphics graphics) { 
       try { 
         Image bgImage = Image.createImage(getWidth(), getHeight()); 

         HttpConnection httpConnection = (HttpConnection) Connector 
             .open("http://stackoverflow.com/content/img/so/logo.png"); 
         Image image = Image.createImage(httpConnection 
             .openInputStream()); 
         bgImage.getGraphics().drawImage(image, 0, 0, 0); 
         httpConnection.close(); 

         graphics.drawImage(bgImage, 0, 0, 0); 
       } catch (IOException e) { 
         e.printStackTrace(); 
       } 
     } 

    } 

} 
 

은 누군가가 말해 주시겠습니까. 교착 상태로 이어질 수있는 스레드가 무엇인지 명확하지 않습니다.

  1. j2me 스레드 모델에 대한 문서가 있습니까?
  2. 어디에서 j2me 시스템 클래스의 소스를 얻을 수 있습니까? (연결 클래스의 구현을 확인하고 싶습니다)?

편집 : 위의 코드에서 나는 논리를 얻습니다. 그러나 아래 코드는 적어도 올바르게 작동해야합니까? 이것은 또한 별도의 스레드에서 네트워크 연결을하고있는 곳에서 교착 상태가됩니다.

 

public class Foo extends MIDlet { 

    protected void destroyApp(boolean arg0) throws MIDletStateChangeException { 
     // TODO Auto-generated method stub 
    } 

    protected void pauseApp() { 
     // TODO Auto-generated method stub 
    } 

    protected void startApp() throws MIDletStateChangeException { 
     Display display = Display.getDisplay(this); 
     MyCanvas myCanvas = new MyCanvas(); 
     display.setCurrent(myCanvas); 
     myCanvas.repaint(); 
    } 

    class MyCanvas extends Canvas { 
     protected void paint(Graphics graphics) { 
      try { 
       Image bgImage = Image.createImage(getWidth(), getHeight()); 

       FetchImage fetchImage = new FetchImage(); 
       Thread thread = new Thread(fetchImage); 
       thread.start(); 

       thread.join(); 

       bgImage.getGraphics().drawImage(fetchImage.image, 0, 0, 0); 

       graphics.drawImage(bgImage, 0, 0, 0); 
      } catch (Exception e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 

    public class FetchImage implements Runnable { 
     public Image image; 

     public void run() { 
      HttpConnection httpConnection; 
      try { 
       httpConnection = (HttpConnection) Connector 
         .open("http://10.4.71.200/stage/images/front/car.png"); 
       image = Image.createImage(httpConnection.openInputStream()); 
       httpConnection.close(); 
      } catch (IOException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 

    } 
} 

답변

9
내가 J2ME 시스템 클래스 (내가 클래스 연결의 구현에서 를 확인하려면)에 대한 소스를 얻을 수

?

당신은 할 수 없습니다. 실제로 벤더에 의존합니다. Nokia가이 상황을 처리하는 방식은 Motorola와 다를 수 있습니다.

배워야 할 교훈은 시스템 콜백에서 값 비싼 계산을하지 않으면 시스템이 응답하지 않을 수 있다는 것입니다. 그래서 sepearate 스레드에서 시간 소모적 인 작업을 수행하고 가능한 한 일찍 콜 백에서 돌아옵니다.

두 번째 예제에서 별도의 스레드를 만들었지 만 paint()에서 완료 될 때까지 기다렸다가 결과적으로 전혀 스레드가 생성되지 않습니다. 당신이 할 수있는

한 가지

class MyCanvas extends Canvas { 

    Image image; 
    boolean imageFetchFailed; 

    protected void paint(Graphics g) { 
     if (image == null) { 
      fetchImage(); 
      g.drawString("Fetching...", getWidth() >> 1, getHeight() >> 1, Graphics.HCENTER | Graphics.TOP) 

     } else if (imageFetchFailed) { 
      g.drawString("Failed to fetch image", getWidth() >> 1, getHeight() >> 1, Graphics.HCENTER | Graphics.TOP) 
     } else { 
      g.drawImage(image, 0, 0, 0); 
     } 
    } 


    private void fetchImage() { 
     new Thread(new Runnable() { 
      public void run() { 
       HttpConnection httpConnection = null; 
       try { 
        httpConnection = (HttpConnection) Connector 
          .open("http://10.4.71.200/stage/images/front/car.png"); 
        image = Image.createImage(httpConnection.openInputStream()); 
       } catch (IOException e) { 
        e.printStackTrace(); 
        imageFetchFailed = true; 
       } 

       if (httpConnection != null) { 
        try { 
         httpConnection.close(); 
        } catch (IOException ignored) { 
        } 
       } 

       // Following will trigger a paint call 
       // and this time image wont be null and will get painted on screen 
       repaint();  
      } 
     }).start(); 
    } 
} 
+0

훌륭한 질문과 답변, 특별히이 스레딩 작업에 대해 명확성을 원한다면. – Sydwell

1

그럼, 기본적인 문제는 일부 Java VM 구현은 모든 일을 같은 자바 스레드를 사용하는 것입니다.

VM의 스레딩 모델에 대해 알아야 할 첫 번째 사항은 누가 그것을 개발했는지입니다. 여기 J2ME 라이선스의 목록이

: http://java.sun.com/javame/licensees/index.jsp

정보에서, 당신의 VM이 사용하고 얼마나 많은 원시 스레드 파악하려고합니다. 2 개의 일반적인 모델은 모든 바이트 코드 해석을 하나의 원시 스레드로 실행하거나 각 Java 스레드를 자체 고유 스레드로 실행합니다.

다음 단계는 기본 운영 체제 API가 비동기적인 방법에 대한 정보를 수집하는 것입니다. VM을 개발할 때 라이센스 사용자는 VM을 운영 체제로 이식하기위한 원시 코드를 작성해야했습니다. 모든 프로세스 간 통신 또는 느린 전송 매체 (플래시 카드에서 GPS 신호로)의 사용은 시스템이 일부 데이터를 기다리는 동안 바이트 코드 인터프리터 스레드가 계속 실행될 수있게하는 별도의 원시 스레드를 사용하여 구현 될 수 있습니다.

다음 단계는 VM이 ​​얼마나 나쁘게 구현되었는지를 깨닫는 것입니다. 일반적으로 VM이 MIDP 사양의 모든 콜백 메서드에 대해 하나의 내부 Java 스레드 만 사용하는 경우 문제가 발생합니다. 따라서 잘못된 자바 스레드에서 열려고하면 연결이 열릴 때까지 키패드 이벤트에 반응 할 기회가 없습니다.

예를 들어, javax.microedition.media.PlayerListener.playerUpdate()와 같은 자바 스레드에서 Canvas.paint()가 호출되기 때문에 화면이 새로 고쳐지는 것을 실제로 방지 할 수 있습니다.

VM 구현 관점에서 황금 규칙은 사용자가 제어하지 않는 콜백 (청취자와 같은 "사용자"코드로 끝날 수 있기 때문에)은 차단 해제를 사용하는 동일한 자바 스레드에서 호출 할 수 없다는 것입니다 표준 API 호출. 많은 VM이이 규칙을 어기면 JavaME 개발자가이 문제를 해결할 것을 권장합니다.

+0

감사합니다. 회신에 따라 질문을 업데이트했습니다. – Abhi

1

Canvas.paint()는 event delivery method이며 이는 시스템 이벤트 스레드에 의해 호출됨을 의미합니다.

시스템에서 Canvas.paint() 호출과 사용자 확인 이벤트 처리가 UI 이벤트 스레드 (UT)에 의해 수행되도록 구현되어 있다고 가정합니다.

이제 UT가 Connector.open()에 의해 Canvas.paint()에서 차단 될 때 UT는 다음에 오는 이벤트 (이 경우 Connector.open()에 의해 트리거 된 사용자 확인 이벤트)를 처리 할 수 ​​없습니다. . UT가 응용 프로그램 코드 내에서 차단 된 다른 이벤트를 처리하는 것은 불가능합니다.

그 이유는 교착 상태가 발생하여 연결 스레드가 발생하지 않을 것으로 기다리고 있으며, UT를 영원히 차단합니다.

일반적으로 시스템 이벤트 스레드가 어떻게 구현 될지 예상하지 말고 가능한 한 빨리 이벤트 처리 메소드에서 복귀하려고 시도해야합니다. 그렇지 않으면 이와 같은 성능 저하 또는 교착 상태가 발생할 수 있습니다.

+0

감사합니다. 나는 지금 그것을 얻는 것 같아. 코드를 재정렬하면 어떻게 작동합니까? 그들은 그것이 이미지를 네트워크에서 가져온 후에 시스템이 paint 메서드를 호출하도록 만드는 것을 보았습니다. 그것을 할 수있는 더 좋은 방법이 있습니까? 내가 직면하고있는 문제와 관련된 표준 패턴이 있습니까? 죄송합니다. J2ME 및 UI 관련 초보자입니다. – Abhi

+0

Manoj가 좋은 예를 보여주었습니다. thread.join()이 Canvas.paint() 내부의 UT를 차단했음을 알았습니다. 가능한 한 빨리 이벤트 콜백에서 복귀하고 이벤트 스레드가 다음 이벤트를 처리하도록하십시오. – tingyu

0

좋은 아이디어이지만, 마노의 예에서 경쟁 조건이 있음을 보인다.

이미지를 다운로드하는 동안 여러 페인트 호출이 가능하여 동일한 이미지를 모두 다운로드하는 여러 스레드가 생성됩니다 (추가 페인트 호출의 한 예로 HTTP 연결 프롬프트가 나타나는 경우).

모든 페인트 호출이 동일한 스레드에서 수행되므로 페인트 호출 내에서 플래그를 테스트하고 설정하여 동기화를 피할 수 있습니다.

class MyCanvas extends Canvas { 

    Image image; 
    boolean imageDownloadStarted; 
    boolean imageFetchFailed; 

    protected void paint(Graphics g) { 
     g.fillRect(0, 0, g.getClipWidth(), g.getClipHeight()); 
     if (image == null) { 
      if (imageDownloadStarted) 
       return; 
      imageDownloadStarted = true; 
      fetchImage(); 
      g.drawString("Fetching...", getWidth() >> 1, getHeight() >> 1, Graphics.HCENTER | Graphics.TOP); 

     } else if (imageFetchFailed) { 
      g.drawString("Failed to fetch image", getWidth() >> 1, getHeight() >> 1, Graphics.HCENTER | Graphics.TOP); 
     } else { 
      g.drawImage(image, 0, 0, 0); 
     } 
    } 

    private void fetchImage() { 
     new Thread(new Runnable() { 

      public void run() { 
       try { 
        final HttpConnection httpConnection = (HttpConnection) Connector.open("http://stackoverflow.com/content/img/so/logo.png"); 
        try { 
         final InputStream stream = httpConnection.openInputStream(); 
         try { 
          image = Image.createImage(stream); 
         } finally { 
          stream.close(); 
         } 
        } finally { 
         httpConnection.close(); 
        } 
       } catch (IOException e) { 
        e.printStackTrace(); 
        imageFetchFailed = true; 
       } 

       repaint(); 
      } 
     }).start(); 
    } 
} 

참고 최종 핵심 단어의 사용 널 테스트 및 위한 openInputStream에 의해 반환 된 스트림의 명시 적 폐쇄를 방지하기 : 아래의 향상된 버전의 시도이다.