这是一个java聊天应用程序。
Structure of program :
1. Main thread to connect server(and listen to keyboard input)
2. I have used EDT to initialize GUI and for action listener
3. I have started another thread from main thread to grab server messages
我有一个JEditorPane
我从主线程和第二线程更新。我已经使用EDT进行更新,但它仍然被冻结了。我无法弄清楚我在哪里做错了或者我需要重构我的代码
public class Client extends JFrame implements Runnable,KeyListener
{
private static final long serialVersionUID = 1L;
static Socket clientSocket=null;
static BufferedReader is=null, in=null;
static PrintStream os=null;
static boolean closed=false;
static boolean go=false;
private JPanel contentPane;
private JComboBox comboT1;
private JComboBox comboT2;
private JComboBox comboT3;
static JEditorPane textArea = new JEditorPane();
static JTextField write;
private JButton send;
public Client()
{
setResizable(false);
setTitle("myStats - Client");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 500, 350);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(new BorderLayout(0, 0));
//textArea = new JTextArea("");
JScrollPane scroll = new JScrollPane(textArea);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
//scroll.setVerticalScrollBarPolicy(ScrollPane.SCROLLBARS_ALWAYS);
contentPane.add(scroll, BorderLayout.CENTER);
write = new JTextField();
write.addKeyListener(this);
send = new JButton("Send");
//JLabel writeSomething = new JLabel("Want to send something --");
Box Vbox = Box.createVerticalBox();
Box Hbox = Box.createHorizontalBox();
Hbox.add(write);
Hbox.add(Box.createRigidArea(new Dimension(5,0)));
Hbox.add(send);
Vbox.add(Box.createRigidArea(new Dimension(0,7)));
Vbox.add(Hbox);
contentPane.add(Vbox, BorderLayout.SOUTH);
send.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
//Add my statistics
go = true;
}
});
}
public static void main(String args[])
{
try
{
clientSocket = new Socket("127.0.0.1",8189);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
os = new PrintStream(clientSocket.getOutputStream(),true);
}
catch(Exception e)//listening
{
System.out.println("Error : "+e.getMessage());
}
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Client frame = new Client();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
{
if(clientSocket!=null && os!=null && in!=null)
{
try
{
new Thread(new Client()).start();
while(!closed) //For Keyboard Input -- Message Box
{
while(!go);
os.println(write.getText());
write.setText("");
go = false;
}
os.close();
in.close();
clientSocket.close();
}
catch(Exception e)
{
System.out.println(e);
DoEverything("Error : "+e.getMessage());
}
}
}
}
public void run()
{
String response;
try
{
while((response=in.readLine())!=null)
{
System.out.println(response);
DoEverything(response);
if(response.trim().equalsIgnoreCase("Bye"))
break;
}
closed = true;
}
catch(Exception e)
{
System.out.println("Error : "+e.getMessage());
DoEverything("Error : "+e.getMessage());
}
}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER)
{
if(write.getText().length()>0)
send.doClick();
}
}
public static void DoEverything(final String mynew) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
textArea.setText(textArea.getText() +""+ mynew+"\n");
}
});
}
}
这是我的服务器代码
public class Server
{
static Socket clientSocket=null;
static ServerSocket serverSocket=null;
static ClientThread threadCollection[] = new ClientThread[20];
public static void main(String args[])
{
int i;
try
{
serverSocket = new ServerSocket(8189); //Blocking Call -- Wait for Client
}
catch(Exception e)
{
System.out.println("Error : "+e.getMessage());
}
while(true)
{
try
{
clientSocket = serverSocket.accept(); //get the client details (socket) to interact with them
for(i=0;i<20;i++)
{
if(threadCollection[i]==null)
{
threadCollection[i] = new ClientThread(clientSocket, threadCollection);
threadCollection[i].start(); //give client server thread(representative)
break;
}
}
}
catch(Exception e)
{
System.out.println("Error : "+e.getMessage());
}
}
}
}
class ClientThread extends Thread
{
PrintStream os=null; //send the message to client
BufferedReader is=null; //receive message from client
Socket clientSocket=null;//client address
ClientThread threadCollection[];//all the clients server got ~20
public ClientThread(Socket cs, ClientThread []t)
{
this.clientSocket = cs;
this.threadCollection = t;
}
public void run()
{
String line = new String();
int i;
try
{
is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
os = new PrintStream(clientSocket.getOutputStream(),true);
os.println("<Server> : Hello "+clientSocket.getInetAddress().getHostName()); //server sending message to client
//name = is.readLine(); //server reading the message from client
//os.println("Welcome '"+name+"' to chat room!");
for(i=0;i<20;i++)
if(threadCollection[i]!=null && threadCollection[i]!=this)
threadCollection[i].os.println("A new user '"+clientSocket.getInetAddress().getHostName()+"' joinied to accomplish today's task");
while(true)
{
line = is.readLine();
System.out.println("<"+clientSocket.getInetAddress().getHostName()+"> : "+line);
if(line.trim().equalsIgnoreCase("Bye"))
break;
for(i=0;i<20;i++)
if(threadCollection[i]!=null)
threadCollection[i].os.println("<"+clientSocket.getInetAddress().getHostName()+">: "+line);
}
for(i=0;i<20;i++)
if(threadCollection[i]!=null && threadCollection[i]!=this)
threadCollection[i].os.println("User '"+clientSocket.getInetAddress().getHostName()+"' has given up!!!");
os.println("Bye "+clientSocket.getInetAddress().getHostName());
for(i=0;i<20;i++)
if(threadCollection[i]==this)
threadCollection[i]=null;
is.close();
os.close();
clientSocket.close();
}
catch(Exception e)
{
System.out.println("Error : "+e.getMessage());
}
}
}
答案 0 :(得分:0)
您应该声明go
和closed
volatile:
static volatile boolean closed=false;
static volatile boolean go=false;
这使得每次访问时都可以读取实际变量。如果没有volatile
,一个线程可能实际上不会访问原始变量,而是使用另一个值(在时间上早些时候有效)。
循环while (!go);
是一个很大的禁忌(带感叹号)。除此之外,如果没有volatile
,它可能最终会使用go
的过时值,而不会同时检查go变量的实际值。 (简单来说,每个线程都有自己的变量副本,你需要synchronized
块来让线程查找真实的东西。)或者你使变量volatile变成了变量&#34; unached& #34;
使用while循环,你想等待。您可以给CPU一些空闲时间来做其他事情而不仅仅占用所有当前的核心:
while (!go) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
}
之后&#34;而(!go)&#34;你打电话给write.setText("");
,但这不在美国东部时间。 (好吧,它可能在EDT中,如果从EDT内部调用main
,但通常情况并非如此。)你需要为EDT包装它。
这可能足以消除冻结。 多线程很难。你应该学习wait / notify(仍然很难),特别是看看java.util.concurrent中的类(稍微不那么难)。