使用 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);
}
}
这也意味着所有的写请求都是排队的,你不需要再担心写部分的线程安全问题。
我在 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);
}
}
这也意味着所有的写请求都是排队的,你不需要再担心写部分的线程安全问题。