3

いくつかの初期化(JTextAreaオブジェクトの作成を含む)を実行し、3つの別々のスレッドを開始し、次にこれらのスレッドがJTextArea(つまりappend()、それに)更新しようとするコードがありますが、まったく機能しません。何も表示されませんJTextArea(ただし、初期化中に、いくつかのテスト行を印刷します。これは正常に機能します)。どうしたの?どうすればこれを修正できますか?また、これらの各スレッドは、を更新する必要があるたびにランダムな時間スリープしJTextAreaます。

申し訳ありませんが、コードを提供していません。すべてが複数のファイルに分散しています。

4

5 に答える 5

4

APIはJTextArea#append(...)がスレッドセーフであると述べていると思いますが、問題があると聞いたので、EDTでのみ呼び出すことをお勧めします。この典型的な例は、SwingWorkerを使用し、publishを呼び出してプロセスメソッドのJTextAreaに追加することです。

私にとっては、コードがなくても具体的な提案をするのは難しいでしょう。コードのどこかでEDTをスリープ状態にしているのかどうか疑問に思う必要があります。

編集:あなたのコメントに従って、このチュートリアルをチェックしてください:Swingの並行性


編集2: Tim Perryのコメントによると、スレッドセーフの喪失とその背後にある理由は、このJavaバグに投稿されており、JTextAreaのドキュメントにテキストが追加されるこのコード行と関係があります。

doc.insertString(doc.getLength(), str, null);

この行は2つの行に分解されます。

  1. int len=doc.getLength();
  2. doc.insertString(len,str,null);

問題は、Document、doc、が1行目と2行目の間で変更された場合、特にドキュメントの長さが問題になる可能性があることです。

于 2011-02-01T23:12:48.700 に答える
3

Java 1.6では、のドキュメントにJTextArea.appendは次のように書かれています。

指定されたテキストをドキュメントの最後に追加します。モデルがnullの場合、または文字列がnullまたは空の場合は何もしません。

ほとんどのSwingメソッドはそうではありませんが、このメソッドはスレッドセーフです。詳細については、スレッドの使用方法を参照してください。

JDK7では、2番目の部分が欠落しています。

指定されたテキストをドキュメントの最後に追加します。モデルがnullの場合、または文字列がnullまたは空の場合は何もしません。

Documentインターフェイス(ユーザー提供のインスタンスを使用できます)を見るとJTextArea、実装がスレッドセーフであっても、スレッドセーフな方法でテキストを追加する方法はありません。スイングスレッディングが壊れています。Swingコンポーネントの近くに行くときは、AWTEDTに固執することを強くお勧めします。

于 2011-02-02T11:37:57.167 に答える
2

Document私は、スレッドセーフを信じることについて警告する経験豊富な人を信頼しています。ただし、アプリケーションがこの問題を簡単に悪用して、何も表示されないということは信じられJTextAreaません。たぶん、他のいくつかの方法が使用されますがappend、それらは一般的な失敗を引き起こします。Oracle jre 6を搭載したDebian(およびjava 6 64ビットを搭載したWin7)で実行するテストアプリを添付しましたが、問題はありません。

開発プロセス中に、次のようないくつかの間違いを修正する必要がありました。

  1. 同期がとれておらずgetLength()insertString()挿入の配置が間違っていて、さらにはBadLocationExceptionsが発生した方法。他のスレッドは、これら2つの命令の間でドキュメントを変更していました。それらが同じ行にあったとしても:)
  2. 飲み込むBadLocationException。打つことは不可能だと確信していましたが、間違っていました。

上記、特にgetLength()とペアのクリティカルセクションを作成する必要性を認識した後、それが失敗することはinsertString()明らかです(ここでトムホーティンの答えを参照してください)。そして、すべてが正常に実行されたわけではなく、結果のテキストが本来よりも短かったため、実際に実行されたことがわかりました。ただし、この問題はループカウント10000では発生せず、100000のみでした。また、基になるドキュメントを変更するだけのJTextArea.appendのjdk 7コードを調べると、外部で同期することもできるようです。JTextAreainsertStringJTextArea

0での挿入も、同期なしでうまく機能しましたが、完了するまでに何年もかかりました。

通常、このようなアプリケーションでは、最後の行までスクロールする必要があります。ねえ、これはawtです。setCaretPositionEDTから抜け出すことはできません。だから私はしません。手動でスクロールします。スクロールを解決するための私の提案は別の答えにあります。

システムでこのアプリケーションに問題が発生した場合は、コメントしてください。私の結論は、Document.insertStringスレッドセーフであるということですが、それを効果的に使用するにはgetLength、同期が必要です。

次のコードPlainDocumentでは、同期メソッドを作成するためにサブクラス化されています。appendこのメソッドは、ラップgetLengthinsertStringてロックになります。このロックはアクセスを保護しているので、別のクラスなしでは使用できませんでした。ただし、外部同期でも正しい結果が得られました。

ところで:多くの編集をしてすみません。最後に、私はさらに学んだ後、この答えを再構築しました。

コード:

import java.awt.*;
import java.util.concurrent.CountDownLatch;
import javax.swing.*;
import javax.swing.text.*;

class SafePlainDocument extends PlainDocument
{
  public void append(String s)
  {
    writeLock();
    try {
      insertString(getLength(), s,  null);
    }
    catch (BadLocationException e) {
      e.printStackTrace();
    }
    finally
    {
      writeUnlock();
    }
  }
}

public class StressJText
{
  public static CountDownLatch m_latch;
  public static SafePlainDocument m_doc;
  public static JTextArea m_ta;

  static class MyThread extends Thread
  {
    SafePlainDocument m_doc;
    JTextArea m_ta;

    public MyThread(SafePlainDocument doc)
    {
      m_doc = doc;
    }

    public void run() 
    {
      for (int i=1; i<=100000; i++) {
        String s = String.format("%19s %9d\n", getName(), i);
        m_doc.append(s);
      }
      StressJText.m_latch.countDown();
    }
  }

  public static void main(String sArgs[])
  {
    System.out.println("hello");
    final int cThreads = 5;
    m_latch = new CountDownLatch(cThreads);
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
          JFrame frame = new JFrame();
          m_ta = new JTextArea();
          m_doc = new SafePlainDocument();
          m_ta.setDocument(m_doc);
          m_ta.setColumns(50);
          m_ta.setRows(20);
          JScrollPane scrollPane = new javax.swing.JScrollPane();
          scrollPane.setViewportView(m_ta);
          frame.add(scrollPane);
          frame.pack();
          frame.setVisible(true);

          for (int it=1; it<=cThreads; it++) {
            MyThread t = new MyThread(m_doc);
            t.start();
          }
        }
    });
    try {
      m_latch.await();
    }
    catch (InterruptedException ie) {
      ie.printStackTrace();
    }
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
          System.out.println("tf len: " + m_ta.getText().length());
          System.out.println("doc len: " + m_doc.getLength());
          System.exit(0);
        }
    });
  }
}
于 2012-10-07T13:34:56.900 に答える
2

JTextArea.append(..)はスレッドセーフなので、別のスレッドから呼び出しても安全です。

ただし、.append()状態のjavadoc:

Does nothing if the model is null or the string is null or empty.

したがって、JTextAreaのモデルが(適切なコンストラクターを介して)初期化されていることを確認してください。

于 2011-02-01T23:23:29.130 に答える
2

JTextAreaスレッドセーフ?

絶対にありません。一般的ではありません。他の人が述べたように、appendメソッドでさえスレッドセーフであるとはもはや文書化されていません。ただし、AbstractDocument.insertStringのJava 7ドキュメントには、このメソッドがスレッドセーフであることが明記されています。

AbstractDocument.insertStringドキュメントによると、使用は安全のようです。そして、それが唯一の合理的な選択肢です。GUIスレッドで文字列モデルを更新すると、パフォーマンスが大幅に低下します。

どうJTextArea.appendですか?私はそれが基礎となるに依存すると思いDocumentます。なぜならPlainDocumentDefaultStyledDocumentそれはスレッドセーフである可能性があるからです。他のモデルについては、関連するドキュメントを調べる必要があります。基になるドキュメントが何であるかわからない場合は、appendスレッドセーフではないものとして扱い、EDTからのみ呼び出す必要があります。

append編集:スレッドセーフではないもう1つの考えられる理由:これは2つの操作で構成されています:getLengthinsertString、および2つの間で、ドキュメントの内容が変更される可能性があります。したがって、のような構成にも注意してinsertString(getLength(), ...)ください。同期がないと正しくありません。AbstractDocument.writeLock役立つかもしれませんが、保護されています。

于 2012-10-07T14:19:59.023 に答える