2016-11-22 76 views
1

我想一次调用一个线程调用swing方法的一些信息。我的JAVA代码线程是否安全,因为代码仅由一个线程调用?

编辑: 我使用Java 7

我看到了以下主题:

Thread Safety of JTextArea.append

我developped一个小型Swing应用程序。

这是我的主类,它是一个线程安全类。 我调用SwingUtilities.invokeLater方法使它成为线程安全类。

MainClass:

package swingex; 

import javax.swing.SwingUtilities; 

public final class MainClass extends Thread { 

    public static void main(String[] _args) { 
     SwingUtilities.invokeLater(new MainClass()); 
    } 

    @Override 
    public void run() { 
     new MainWindow(); 
    } 
} 

这里是一个类从JFrame中继承。 我在内容窗格中放置了一个文本区域和一个按钮。

主窗口:

package swingex; 

import java.awt.Dimension; 

import javax.swing.BoxLayout; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.JTextArea; 

public final class MainWindow extends JFrame { 

    private JTextArea textArea = new JTextArea(32,32); 

    //AddText inherits from Thread class 
    private AddText thread = new AddText(textArea); 

    public MainWindow() { 
     JPanel panel_ = new JPanel(); 
     panel_.setLayout(new BoxLayout(panel_, BoxLayout.PAGE_AXIS)); 
     JScrollPane scr_ = new JScrollPane(textArea); 
     scr_.setPreferredSize(new Dimension(128, 128)); 
     panel_.add(scr_); 
     //The button is used for adding rows in the text area 
     JButton button_ = new JButton("Add rows"); 
     //Adding the event 
     button_.addActionListener(new AddTextEvent(this)); 
     panel_.add(button_); 
     setContentPane(panel_); 
     setDefaultCloseOperation(EXIT_ON_CLOSE); 
     pack(); 
     setVisible(true); 
    } 

    //Called by actionPerformed method 
    public void setText() { 
     if (thread.isAlive()) { 
      //prevent from setting the text area by multi threading 
      return; 
     } 
     //For avoiding issues, the text area is affected by less than two threads. 
     thread = new AddText(textArea); 
     thread.start(); 
    } 
} 

点击按钮使睡在螺纹而有5秒,然后该线程添加200行到文本区。 AddText:

package swingex; 

import java.awt.Rectangle; 

import javax.swing.JTextArea; 
import javax.swing.text.BadLocationException; 

public final class AddText extends Thread { 

    private JTextArea textArea; 

    public AddText(JTextArea _textArea) { 
     textArea = _textArea; 
    } 

    @Override 
    public void run() { 
     try { 
      Thread.sleep(5000); 
     } catch (InterruptedException _0) { 
      _0.printStackTrace(); 
     } 
     //Only one thread can access the code 
     for (int i = 0; i < 200; i++) { 
      textArea.append("Text"+i+"\n"); 
     } 
     int endPosition_ = textArea.getDocument().getLength(); 
     Rectangle bottom_; 
     try { 
      bottom_ = textArea.modelToView(endPosition_); 
      textArea.scrollRectToVisible(bottom_); 
     } catch (BadLocationException _0) { 
      _0.printStackTrace(); 
     } 
    } 
} 

这个类实现的ActionListener,它用于点击按钮。

AddTextEvent:

package swingex; 

import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 

public final class AddTextEvent implements ActionListener { 

    private MainWindow window; 

    public AddTextEvent(MainWindow _window) { 
     window = _window; 
    } 

    @Override 
    public void actionPerformed(ActionEvent _e) { 
     window.setText(); 
    } 

} 

预先感谢您。

编辑2:我的“新主题”类是以下之一:(其他类保持不变的)

package swingex; 

import java.awt.Rectangle; 

import javax.swing.JTextArea; 
import javax.swing.SwingWorker; 
import javax.swing.text.BadLocationException; 

/**Now the class AddText inherits from SwingWorker*/ 
public final class AddText extends SwingWorker<Void, Void> { 

    private JTextArea textArea; 

    public AddText(JTextArea _textArea) { 
     textArea = _textArea; 
    } 


    /**The equivalent in the class Thread*/ 
    public void start() { 
     execute(); 
    } 

    @Override 
    public Void doInBackground() { 
     try { 
      Thread.sleep(5000); 
     } catch (InterruptedException _0) { 
      _0.printStackTrace(); 
     } 
     for (int i = 0; i < 200; i++) { 
      textArea.append("Text"+i+"\n"); 
     } 
     int endPosition_ = textArea.getDocument().getLength(); 
     Rectangle bottom_; 
     try { 
      bottom_ = textArea.modelToView(endPosition_); 
      textArea.scrollRectToVisible(bottom_); 
     } catch (BadLocationException _0) { 
      _0.printStackTrace(); 
     } 
     return null; 
    } 

    /**The equivalent in the class Thread*/ 
    public boolean isAlive() { 
     return !isDone() || !isCancelled(); 
    } 
} 

编辑3:这是一个自定义类从“摇摆”继承组件:

我的自定义方法是“线程安全的”还是不是?

MyTextArea:

package swingex; 

import javax.swing.JTextArea; 

public class MyTextArea extends JTextArea { 

    private String aField = ""; 

    public String getaField() { 
     return aField; 
    } 

    public void setaField(String _aField) { 
     aField = _aField; 
    } 
} 

现在,我使用SwingUtilities类的方法 “的invokeLater” 从SwingWorker的

+1

为什么不使用Swing'Timer'? –

+0

@Catalina岛:我想通过使用类Thread的“睡眠”方法来模拟长时间的治疗。 – cardman

+1

你可以使用'SwingWorker'或'invokeLater()'。 –

回答

1

的 “doInBackground” 的方法,我认为你应该做的Swing的EDT行动。所以睡后的一切应该用invokeLater来调用。

这不是并发访问的问题。我们不应该在其他线程中修改UI。

你可以做的是:

  1. 没有这个动作创建自己的线程在Runnable
  2. 包裹行动和invokeLater调用它。然后在EDT确保正确的订单。

长期以来的行动,看看swingworker

为什么会有问题? 至少有2个线程:

  1. Swing EDT:您创建所有组件的位置,Swing修改其UI。
  2. 线程在后台:您在哪里做了长时间的处理,并且尝试追加文本。在这里您可以访问EDT线程中的对象。
+0

我在“click”事件开始时使用了类Thread的方法isAlive,因此从我的类“AddText”继承的两个线程不能同时访问JTextArea的“append”方法。 – cardman

+0

@cardman:排除你自己对'append'的调用不会阻止用户输入到textarea中,即使它是只读的,Swing在您的后台操作对它进行操作时仍可能会尝试绘制它。这简直就是坏的。如果您只是将其复制到“SwingWorker”中,则您的代码无效。你应该按照预期使用它,这在[它的文档](http://docs.oracle.com/javase/7/docs/api/?javax/swing/SwingWorker.html)中有详细解释。 – Holger

+0

@cardman更新了我的答案。无论如何,请先阅读Swing文档,然后按照建议进行操作。 –