2009-05-15 13 views
7

以下の(抜粋後の)ミッドレットコード(クラス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システムクラスのソースを取得できますか(Connectionクラスの実装をチェックします)

EDIT:上記のコードでは、ロジックが得られます。しかし、以下のコードは少なくとも正しく動作するはずですか?これはまた、別のスレッドでネットワーク接続を行っているところでデッドロックします。

 

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とは異なる場合があります。

あなたが学ばなければならない教訓は、システムコールバックでは高価な計算をしないため、システムが応答しなくなる可能性があることです。したがって、時間のかかる操作をセパレートスレッドに入れ、可能な限り早くコールバックから戻します。

2番目の例では別のスレッドを作成していますが、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の実装がすべてを行うために、同じJavaスレッドを使用することです。

VMのスレッディングモデルについて最初に理解する必要があるのは、誰がそれを開発したのかです。ここではJ2MEのライセンシーのリストがあり

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

その情報からは、あなたのVMが使用しているどのように多くのネイティブスレッドを把握してみてください。 2つの通常のモデルは、すべてのバイトコード解釈を1つのネイティブスレッドに実行するか、または各Javaスレッドを独自のネイティブスレッドに実行します。

次の手順は、基本となるオペレーティングシステムAPIがどのように非同期であるかに関する情報を収集することです。 VMを開発する場合、ライセンシーは、VMをオペレーティングシステムに移植するためのネイティブコードを記述しなければなりませんでした。どのプロセス間通信または低速伝送媒体の使用(フラッシュカードからGPS信号への)は、システムが何らかのデータを待っている間にバイトコードインタプリタスレッドを実行し続けることができる別個のネイティブスレッドを使用して実装することができる。

次のステップは、VMがどれほどひどく実装されているかを認識することです。通常、問題は、VMがMIDP仕様のすべてのコールバックメソッドに対して内部Javaスレッドを1つだけ使用する場合に発生します。したがって、間違ったJavaスレッドで開こうとすると、接続が開かれるまで、キーパッドイベントに反応する機会はありません。

さらに悪いことに、実際には、Canvas.paint()がjavax.microedition.media.PlayerListener.playerUpdate()などの同じJavaスレッドで呼び出されるため、画面がリフレッシュされるのを防ぐことができます。

VM実装の観点からは、制御しないコールバック(リスナーのような「ユーザー」コードになる可能性があるため)は、ブロック解除を使用する同じJavaスレッドから呼び出すことはできません標準APIコール。そこにいる多くのVMがそのルールを破るだけなので、JavaME開発者がそれを回避することを推奨します。

+0

ありがとうございました。あなたの返信に基づいて質問を更新しました。 – Abhi

1

Canvas.paint()はevent delivery methodです。つまり、システムイベントスレッドによって呼び出されます。

システム上で、Canvas.paint()呼び出しとユーザー確認イベント処理の両方がUIイベントスレッド(UT)によって実行されると想定します。

UTがConnector.open()によってCanvas.paint()でブロックされると、UTは次回のイベント(この場合はConnector.open()によってトリガーされるユーザー確認イベント)を処理できません。 。 UTがアプリケーションコード内でブロックされている場合、別のイベントを処理することはできません。

デッドロックが発生するのは、hereです。接続スレッドは何も起こらないことを待っていて、永遠にUTをブロックします。

一般的に、システムイベントスレッドはどのように実装されるのか予想せず、可能な限り迅速にイベント処理メソッドから復帰しようとするべきです。それ以外の場合は、このようなパフォーマンス低下またはデッドロックが発生する可能性があります。

+0

ありがとうございます。私は今それを手に入れていると思います。コードを再配置して機能させるにはどうすればよいでしょうか?彼らは私がそれがイメージがネットワークからフェッチされた後にペイントメソッドを呼び出すことを確認します。それを行うより良い方法はありますか?私が直面している問題とその標準的な解決策に関連するパターンはありますか?申し訳ありませんが、私はJ2MEとUIに関する初心者です。 – Abhi

+0

Manojがあなたに良い例を示しました。thread.join()がCanvas.paint()内のUTをまだブロックしていることに注目してください。イベントコールバックからできるだけ早く戻って、イベントスレッドに次のイベントを処理させてください。 – tingyu

0

いくつかの良いアイデアですが、Manojさんの例では、競合状態があるようです。

イメージのダウンロード中に複数のペイントコールが可能で、複数のスレッドがすべて同じイメージをダウンロードするようになります(追加のペイントコールの例は、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によって返されたストリームの明示的な閉鎖を回避するために最終キーワードの使用:以下は、改良版の試みです。

関連する問題