Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/334.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在多线程Swing程序中频繁调用setText()_Java_Multithreading_Swing - Fatal编程技术网

Java 在多线程Swing程序中频繁调用setText()

Java 在多线程Swing程序中频繁调用setText(),java,multithreading,swing,Java,Multithreading,Swing,我有一个Swing程序,其中工作是在非Swing线程中连续进行的。它经常需要更新JTextPane—经常是每秒多次。我意识到setText需要从事件调度线程的内部调用,但我不知道如何使其顺利进行 下面的最小完整示例与我所能得到的最接近,使用了PipedInputStream/PipedOutputStream对,但这似乎只是每秒左右更新一次屏幕。我不知道怎么花了这么长时间 import java.awt.event.*; import javax.swing.*; import java.io.

我有一个Swing程序,其中工作是在非Swing线程中连续进行的。它经常需要更新JTextPane—经常是每秒多次。我意识到setText需要从事件调度线程的内部调用,但我不知道如何使其顺利进行

下面的最小完整示例与我所能得到的最接近,使用了PipedInputStream/PipedOutputStream对,但这似乎只是每秒左右更新一次屏幕。我不知道怎么花了这么长时间

import java.awt.event.*;
import javax.swing.*;
import java.io.*;

public class TextTest extends JFrame {
    private JTextPane out = new JTextPane();
    private PipedInputStream pIn = new PipedInputStream();
    private PrintWriter pOut;

    public TextTest() {
        try {
            pOut = new PrintWriter(new PipedOutputStream(pIn));
        }
        catch (IOException e) {System.err.println("can't init stream");}

        add(new JScrollPane(out));
        setSize(500, 300);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);

        // Start a loop to print to the stream continuously
        new Thread() {
            public void run() {
                for (int i = 0; true; i++) {
                    pOut.println(i);
                }
            }
        }.start();

        // Start a timer to display the text in the stream every 10 ms
        new Timer(10, new ActionListener() {
            public void actionPerformed (ActionEvent evt) {
                try {
                    if (pIn.available() > 0) {
                        byte[] buffer = new byte[pIn.available()];
                        pIn.read(buffer);
                        out.setText(out.getText() + new String(buffer));
                    }
                }
                catch (IOException e) {System.err.println("can't read stream");}
            }
        }).start();
    }

    public static void main(String[] args) {
        new TextTest();
    }
}
我是不是实施错了?对于如何从EDT外部持续更新JTextPane,我是否完全没有正确的想法?

该方法是线程安全的,尽管大多数Swing方法不是。有关更多信息,请参阅

附录:为了参考,这里有一些关于EDT的更新。另一件需要注意的事情是,的操作事件处理程序在EDT上执行。以下是我的变体:

import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import javax.swing.text.DefaultCaret;

public class TextTest extends JFrame {

    private JTextArea out = new JTextArea();
    private PipedInputStream pIn = new PipedInputStream();
    private PrintWriter pOut;

    public TextTest() {
        try {
            pOut = new PrintWriter(new PipedOutputStream(pIn));
        } catch (IOException e) {
            System.err.println("can't init stream");
        }

        DefaultCaret caret = (DefaultCaret) out.getCaret();
        caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);

        add(new JScrollPane(out));
        setSize(300, 500);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);

        // Start a loop to print to the stream continuously
        new Thread() {

            public void run() {
                for (int i = 0; true; i++) {
                    pOut.println(i);
                }
            }
        }.start();

        // Start a timer to display the text in the stream every 10 ms
        new Timer(10, new ActionListener() {

            public void actionPerformed(ActionEvent evt) {
                try {
                    out.append(String.valueOf((char) pIn.read()));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public static void main(String[] args) {
        new TextTest();
    }
}
该方法是线程安全的,尽管大多数Swing方法不是。有关更多信息,请参阅

附录:为了参考,这里有一些关于EDT的更新。另一件需要注意的事情是,的操作事件处理程序在EDT上执行。以下是我的变体:

import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import javax.swing.text.DefaultCaret;

public class TextTest extends JFrame {

    private JTextArea out = new JTextArea();
    private PipedInputStream pIn = new PipedInputStream();
    private PrintWriter pOut;

    public TextTest() {
        try {
            pOut = new PrintWriter(new PipedOutputStream(pIn));
        } catch (IOException e) {
            System.err.println("can't init stream");
        }

        DefaultCaret caret = (DefaultCaret) out.getCaret();
        caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);

        add(new JScrollPane(out));
        setSize(300, 500);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);

        // Start a loop to print to the stream continuously
        new Thread() {

            public void run() {
                for (int i = 0; true; i++) {
                    pOut.println(i);
                }
            }
        }.start();

        // Start a timer to display the text in the stream every 10 ms
        new Timer(10, new ActionListener() {

            public void actionPerformed(ActionEvent evt) {
                try {
                    out.append(String.valueOf((char) pIn.read()));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    public static void main(String[] args) {
        new TextTest();
    }
}
但这似乎只是每秒钟左右更新一次屏幕。我不知道怎么花了这么长时间

import java.awt.event.*;
import javax.swing.*;
import java.io.*;

public class TextTest extends JFrame {
    private JTextPane out = new JTextPane();
    private PipedInputStream pIn = new PipedInputStream();
    private PrintWriter pOut;

    public TextTest() {
        try {
            pOut = new PrintWriter(new PipedOutputStream(pIn));
        }
        catch (IOException e) {System.err.println("can't init stream");}

        add(new JScrollPane(out));
        setSize(500, 300);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);

        // Start a loop to print to the stream continuously
        new Thread() {
            public void run() {
                for (int i = 0; true; i++) {
                    pOut.println(i);
                }
            }
        }.start();

        // Start a timer to display the text in the stream every 10 ms
        new Timer(10, new ActionListener() {
            public void actionPerformed (ActionEvent evt) {
                try {
                    if (pIn.available() > 0) {
                        byte[] buffer = new byte[pIn.available()];
                        pIn.read(buffer);
                        out.setText(out.getText() + new String(buffer));
                    }
                }
                catch (IOException e) {System.err.println("can't read stream");}
            }
        }).start();
    }

    public static void main(String[] args) {
        new TextTest();
    }
}
我将上述语句添加到计时器的actionPerformed代码中。在缓冲区达到1024字节之前,不会发生任何事情。所以我想你需要改变缓冲区的大小

此外,您不应该使用setText。每次进行更改时重新创建文档效率很低

您可以使用:

out.replaceSelection(new String(buffer) );
或者更常见的方法是使用:

Document doc = textPane.getDocument();
doc.insertString("...", doc.getLength(), null);
不要认为insertString方法是线程安全的,但replaceSelection方法是

编辑:

只是尝试在输入流中使用大小为10的缓冲区并刷新输出流,但没有任何区别,所以我想我不理解管道流

但这似乎只是每秒钟左右更新一次屏幕。我不知道怎么花了这么长时间

import java.awt.event.*;
import javax.swing.*;
import java.io.*;

public class TextTest extends JFrame {
    private JTextPane out = new JTextPane();
    private PipedInputStream pIn = new PipedInputStream();
    private PrintWriter pOut;

    public TextTest() {
        try {
            pOut = new PrintWriter(new PipedOutputStream(pIn));
        }
        catch (IOException e) {System.err.println("can't init stream");}

        add(new JScrollPane(out));
        setSize(500, 300);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);

        // Start a loop to print to the stream continuously
        new Thread() {
            public void run() {
                for (int i = 0; true; i++) {
                    pOut.println(i);
                }
            }
        }.start();

        // Start a timer to display the text in the stream every 10 ms
        new Timer(10, new ActionListener() {
            public void actionPerformed (ActionEvent evt) {
                try {
                    if (pIn.available() > 0) {
                        byte[] buffer = new byte[pIn.available()];
                        pIn.read(buffer);
                        out.setText(out.getText() + new String(buffer));
                    }
                }
                catch (IOException e) {System.err.println("can't read stream");}
            }
        }).start();
    }

    public static void main(String[] args) {
        new TextTest();
    }
}
我将上述语句添加到计时器的actionPerformed代码中。在缓冲区达到1024字节之前,不会发生任何事情。所以我想你需要改变缓冲区的大小

此外,您不应该使用setText。每次进行更改时重新创建文档效率很低

您可以使用:

out.replaceSelection(new String(buffer) );
或者更常见的方法是使用:

Document doc = textPane.getDocument();
doc.insertString("...", doc.getLength(), null);
不要认为insertString方法是线程安全的,但replaceSelection方法是

编辑:


只是尝试在输入流中使用大小为10的缓冲区并刷新输出流,但没有任何区别,因此我想我不理解管道流。

有关并发性和Swing的正确教程链接如下所示:

@camickr:setText不会创建新文档,它有效地执行以下操作之一:

doc.replace(0, doc.getLength(), s, null);
或者这个:

doc.remove(0, doc.getLength());
doc.insertString(0, s, null);
我并不是说它很有效,但是


但是,setText不做的另一件事是使setDocument发出重新验证和重新绘制。在调用setText之后添加这两个调用可能是值得的。

有关并发性和Swing的正确教程链接如下所示:

@camickr:setText不会创建新文档,它有效地执行以下操作之一:

doc.replace(0, doc.getLength(), s, null);
或者这个:

doc.remove(0, doc.getLength());
doc.insertString(0, s, null);
我并不是说它很有效,但是


但是,setText不做的另一件事是使setDocument发出重新验证和重新绘制。在调用setText之后添加这两个调用可能是值得的。

您需要刷新printWriter的输出,我建议您的线程稍微暂停一下,因为它是一个紧密的for循环,让更新线程偶尔启动一次

 pOut.println(i); 
 pOut.flush();
 try {
      sleep(10);
 } catch (InterruptedException e) {
 }

这将提供一个更平滑的流程。

您需要刷新printWriter的输出,我建议您的线程稍微暂停一下,因为它是一个紧密的for循环,让更新线程偶尔启动一次

 pOut.println(i); 
 pOut.flush();
 try {
      sleep(10);
 } catch (InterruptedException e) {
 }

这将提供一个更平滑的流程。

@jjnguy:我不知道为什么API链接是404 oracled,但是版本7已经放弃了线程安全限制;请考虑恢复你的答案,因为它可能是更好的选择。我不知道!我也不确定它是否解决了这个问题——这两个线程仍然以某种方式相互碰撞。我尝试切换到StringBuilder,并从工作线程重复调用setText,但显示闪烁,JScrollPane从不滚动到底部。如果您愿意,我可以发布更新的代码,但我想我仍然需要一个从EDT更新的解决方案。@Etaoin:As@camickr notes,您看到的是缓冲。拿出它,更新就会顺利进行。对于简单的pIn码+1。读一下,我为什么没想到呢?。而且,使用JTextArea比使用JTextPane效率更高,因为您不必担心属性
这是一个很好的例子。太好了,谢谢你@jjnguy:我不知道为什么API链接是404Oracled,但是版本7已经放弃了线程安全的限制条件;请考虑恢复你的答案,因为它可能是更好的选择。我不知道!我也不确定它是否解决了这个问题——这两个线程仍然以某种方式相互碰撞。我尝试切换到StringBuilder,并从工作线程重复调用setText,但显示闪烁,JScrollPane从不滚动到底部。如果您愿意,我可以发布更新的代码,但我想我仍然需要一个从EDT更新的解决方案。@Etaoin:As@camickr notes,您看到的是缓冲。拿出它,更新就会顺利进行。对于简单的pIn码+1。读一下,我为什么没想到呢?。此外,使用JTextArea比使用JTextPane效率更高,因为您不必担心属性。太好了,谢谢你!JTextArea和append是另一种选择。谢谢,这看起来很有希望!奇怪的是,我试着将缓冲区大小调低到1,作为一个测试——更新的速度完全相同,只是现在每次更新一个字符。所以我不认为是缓冲区填满了。对不起,如果我暴露了我的无知;我以前从未尝试过使用PipedInputStream/PipedOutStream。@Etaoin,请参阅垃圾神的更新,以便在没有缓冲区的情况下读取数据。JTextArea和append是另一种选择。谢谢,这看起来很有希望!奇怪的是,我试着将缓冲区大小调低到1,作为一个测试——更新的速度完全相同,只是现在每次更新一个字符。所以我不认为是缓冲区填满了。对不起,如果我暴露了我的无知;我以前从未尝试过使用PipedInputStream/PipedOutStream。@Etaoin,请参阅垃圾神的更新,以在没有缓冲区的情况下读取数据。这实际上会创建一个新文档,因为您丢失了文档结构,即创建用于表示文档的元素。此外,可能与之关联的任何属性也将丢失。然后,您需要重新分析整个文本以重建文档结构。实际上,这会创建一个新文档,因为您会丢失文档结构,即创建用于表示文档的元素。此外,可能与之关联的任何属性也将丢失。然后需要重新分析整个文本以重建文档结构。