这几天做了一个基于socket的宠物商店程序,其中服务端的图形化界面问题困扰了很久。今天终于解决,因此也对多线程有了更好的理解。
想很多新手一样,我其实犯了一个错误。
由于soket服务端端口监听用的是 while(true)循环导致,GUI点击开启服务后,主线程被阻塞在while(true)上,是GUI界面失效。
找到了问题所在,那么就用多线程解决。把监听端口放到线程里,这样就不会阻塞主线程画GUI界面。
由于没搞清楚 start()和run(),的区别,结果又出事了。在启动按钮事件里 直接调用了线程的run(),结果点击启动按钮后,GUI界面依然失效。
这就说明主线程依然被阻塞到while(true)里面了,为什么呢?
仔细研究发现run()方法并没有真正的触发线程,run只是调用的 线程中的实现,实际上,程序本身依然是单线程的。所以按钮调用run()依然会阻塞主线程。
将run()改为start()问题就解决了,start()是触发线程,真正的实现了多线程。这样接听端口的while()在副线程里,并不会阻塞主线程。这样GUI不会再
失效了。
这是修改后的代码:
package server;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Server_GUI {
Server server = new Server();
JFrame jf = new JFrame("宠物店服务器V1.0");
private JTabbedPane tabs = new JTabbedPane();
private JTextField txt = new JTextField();
private JButton StartServer = new JButton("启动服务器");
private JButton StopServer = new JButton("关闭服务器");
private ButtonPanel managerPanel = new ButtonPanel("服务的开启与关闭",
new String[] {});
private ButtonPanel informationPanel = new ButtonPanel("服务的版本信息",
new String[] {});
private JPanel manager = new JPanel();// 管理面板
private JPanel information = new JPanel();// 版本面板
public void init() {
StartServer.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
JOptionPane.showMessageDialog(null, "服务已开启!", "服务启动",
JOptionPane.DEFAULT_OPTION);
server.start();
}
});
StopServer.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
JOptionPane.showMessageDialog(null, "服务已关闭!", "服务关闭",
JOptionPane.DEFAULT_OPTION);
System.exit(0);
}
});
// 管理界面
JPanel panel_1 = new JPanel();
panel_1.add(StartServer);
JPanel panel_2 = new JPanel();
panel_2.add(StopServer);
Box managertop = Box.createVerticalBox();
managertop.add(panel_1);
managertop.add(panel_2);
managerPanel.add(managertop);
manager.add(managerPanel);
tabs.add("管理界面", manager);
// 版本信息
JPanel panel4_1 = new JPanel();
JPanel panel4_2 = new JPanel();
JPanel panel4_3 = new JPanel();
JPanel panel4_4 = new JPanel();
JPanel panel4_5 = new JPanel();
JPanel panel4_7 = new JPanel();
JPanel panel4_9 = new JPanel();
Box topInformation = Box.createVerticalBox();
topInformation.add(panel4_1.add(new JLabel("宠物网络服务器端V1.0")));
topInformation.add(panel4_2.add(new JLabel("主要功能:")));
topInformation.add(panel4_3.add(new JLabel("宠物信息的增、删、改、查!")));
topInformation.add(panel4_5.add(new JLabel("宠物信息动态存储,服务器同步!")));
topInformation.add(panel4_4.add(new JLabel("2010届—网络15班")));
topInformation.add(panel4_7.add(new JLabel("姓名:仲强-学号:53101512")));
topInformation.add(panel4_9.add(new JLabel("2012年4月25日 QQ:6475005")));
informationPanel.add(topInformation);
information.add(informationPanel);
tabs.add("版本信息", information);
// tabs 页签选择监听器
tabs.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
if (tabs.getSelectedIndex() == 0) {
txt.setText("管理界面");
} else {
txt.setText("版本信息");
}
}
});
Container contentPane = jf.getContentPane();
contentPane.add(BorderLayout.SOUTH, txt);
contentPane.add(tabs);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
@SuppressWarnings("serial")
class ButtonPanel extends JPanel {
private ButtonGroup group;
public ButtonPanel(String title, String[] options) {
setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(), title));
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
group = new ButtonGroup();
for (int i = 0; options != null && i < options.length; i++) {
JRadioButton b = new JRadioButton(options[i]);
b.setActionCommand(options[i]);
add(b);
group.add(b);
b.setSelected(i == 0);
}
}
// 定义一个方法,用于返回用户选择的选项
public String getSelection() {
return group.getSelection().getActionCommand();
}
}
public static void main(String[] args) {
File f = new File("users_pet");
if (!f.exists()) {
f.mkdirs();
}
new Server_GUI().init();
}
public class Server extends Thread {
@Override
public void run() {
ServerMain server = new ServerMain();
server.StartServer();
}
}
}