使用 CachedThreadPool 写入 JTextArea

Writing to JTextArea with CachedThreadPool

我在 CodeReview 中问过这个问题,但它已关闭。

对于一项学校作业,我必须创建 54 个线程,这些线程 运行 同时从 Executors.newCachedThreadPool() 以线程安全的方式写入 JTextArea。每个线程必须将 'A' 到 'Z' 写入字段 1000 次。无论我将 synchronized 关键字放在哪里,我都无法确保程序线程安全。我有一个 运行nable class 可以进行写入,但我在保持线程安全方面遇到了问题。我尝试过的唯一可行的方法是将 Thread.sleep(500) 放入循环遍历所有字母的循环中,这在我增加迭代次数时不起作用,并且无论如何都会在 Netbeans 中发出警告。

synchronized 关键字放在正确的位置是否会确保线程安全,还是我必须更改线程本身?我在下面的评论中留下了我以前的一些尝试。

我更改了代码,使其运行一次。它 运行 是一个写入 JTextArea 的实例,但我在将其转换为多个同步线程时遇到了问题。

AlphabetThread.java

package threadpool;

import javax.swing.JTextArea;

public class AlphabetThread implements Runnable{

    char letter;
    JTextArea toEdit;
    //final int NUMBER_OF_ITERATIONS = 1000;

    public AlphabetThread(char e, JTextArea tf) {
        letter = e;
        toEdit = tf;
    }

    @Override
    public void run() {
        //for (int i = 0; i < NUMBER_OF_ITERATIONS; i++) {

            toEdit.setText(toEdit.getText() + letter);

    }

//    public synchronized void createThread() {
//        for (int i = 0; i < NUMBER_OF_ITERATIONS; i++) {
//
//            toEdit.setText(toEdit.getText() + letter);
//        }
//    }
}

ThreadPool.java

package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPool {

//    static char letter;
//
//    Thread alphabetThread = new Thread(() -> {
//        char e = letter;
//        mainWindow.textBox.setText(mainWindow.textBox.getText() + e);
//    });
//    public static synchronized AlphabetThread createThread(char e, JTextArea tf) throws InterruptedException {
//        AlphabetThread runMe = new AlphabetThread(e, tf);
//        return runMe;
//    }
    public static void main(String[] args) {
        TextWindow mainWindow = new TextWindow();
        ExecutorService pool = Executors.newCachedThreadPool();

        for (char alphabet = 'A'; alphabet <= 'Z'; alphabet++) {

            AlphabetThread alphabetThread = new AlphabetThread(alphabet, mainWindow.textBox);
            alphabetThread.run();

        }
        pool.shutdown();
    }
}

TextWindow.java

package threadpool;

import java.awt.Dimension;
import java.awt.HeadlessException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;

public class TextWindow extends JFrame {

    public JPanel mainPanel = new JPanel();
    public JTextArea textBox = new JTextArea();

    public TextWindow() throws HeadlessException {

        add(mainPanel);
        textBox.setPreferredSize(new Dimension(450,450));
        textBox.setVisible(true);
        mainPanel.add(textBox);
        setVisible(true);
        setSize(500, 500);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        pack();
    }

}

不需要完整的解决方案(尽管它会很好)。我更喜欢一些关于在哪里更正代码的指示。

您的代码有几个问题。首先,一般规则是您需要对所有线程使用 same 监视器对象进行同步。此外,您的尝试似乎是在同步创建线程,而不是在写入文本区域时。 如果swing是线程安全的,您可以使用文本区域作为监控对象,例如:

synchronized (toEdit) {
    toEdit.setText(toEdit.getText() + letter);
}

这足以同步写入。但是,swing 不是线程安全的。 这是第二个问题。修改 JComponents 只能在事件调度线程中完成。这是使用 invokeLater() 或(很少)invokeAndWait():

完成的
SwingUtilities.invokeLater(new Runnable() {
    @Override
    public void run() {
        toEdit.setText(toEdit.getText() + letter);
    }
}

这也意味着所有的写请求都是排队的,你不需要再担心写部分的线程安全问题。