这个线程问题 怎么也想不明白 求助!

windyfzz 2011-07-13 09:15:06
做了一个登陆系统。 服务器已做好

如图是一个登陆界面

无标题.png (27.88 KB)



其中我想实现这样一个功能: 当你输入你的昵称后 点击登陆 开始连接服务器。。。 但是如果有别人用相同的昵称登陆服务器,服务器会向客户端发送一个消息"ReName"(重名)。客户端检测到这个信息后 系统会提醒 “你的昵称已被人使用!”要求重新输入新的昵称 如果检测到昵称没有被使用 则进入 登陆成功的界面。

部分源码如下:
//实时接收服务器传来的消息
package CSsystem;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

public class serverSocket extends Thread
{
//OutputStream os=null;

//PrintStream ps=null;

InputStream is=null;

BufferedReader br=null;

String serverInformation=null;

boolean isHaveReName=false;

Socket socket=null;

public serverSocket(Socket socket)
{
try
{
this.socket=socket;

is=socket.getInputStream();
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));

// os=socket.getOutputStream();
// ps=new PrintStream(os);
}
catch(Exception ex)
{
ex.printStackTrace();
}
}


public void run()
{
while(true)
{
try
{
serverInformation=getInformation();
if(serverInformation.equals("ReName"))
{
//System.out.println("收到重名信息!");
isHaveReName=true;

}
}
catch(Exception ex)
{
ex.printStackTrace();
}


}
}


private String getInformation()
{

String str=null;
try
{
str=br.readLine();

}
catch(Exception ex)
{
ex.printStackTrace();
}
return str;
}


}

点击登陆按钮时事件
/**
登陆
**/
private void landingToServer()
{
if(nameField.getText().trim().length()==0)
{
JOptionPane.showMessageDialog(null, "请输入你的昵称^_^!", "",
JOptionPane.WARNING_MESSAGE);
return;
}

try
{
IP=serverIPAddrField.getText().trim();//这是要输入的服务器端 的IP地址

sportNumber=Integer.parseInt(sportNumberField.getText().trim());//这是连接的端口号

socket=new Socket();
socket.connect(new InetSocketAddress(InetAddress.getByName(IP),sportNumber), 15*1000);

if(socket.isConnected())//如果连接服务器成功
{

ss=new serverSocket(socket);//ss为serverSocket类的全局变量(已在前面定义过)
ss.start();

if(ss.isHaveReName==true)//通过检测isHaveReName值判断是否重名
{

JOptionPane.showMessageDialog(null, "你的昵称已被人使用!", "",
JOptionPane.WARNING_MESSAGE);
}
else//否则进入登陆成功的界面
{
createChatPanel();
resetPanel(chatPanel);
}

}


}

catch()
{
。。。。。。
}


经过测试 当如果重名的时候 服务器能向客户端发送"ReName"消息 客户端也能接收到该消息。

我实现的方法是 在serverSocket类中定义一个boolean isHaveReName=false变量 当接收到"ReName"消息是说明有重名,此时isHaveReName=true;

在登陆事件函数中通过检测 isHaveReName的值判断是否重名。 但是测试的时候isHaveReName的值总是false; 但是当接收到"ReName"消息时,在serverSocket类中打印该值的时候该值为true,正常。 为什么像我那样在外面定义一个serverSocket类的变量 然后打印该值时即使接收到了"ReName"消息 该值还是总是false呢? 很困惑 可能是我哪里犯糊涂了没想明白。。。
求解答
...全文
135 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
qybao 2011-07-13
  • 打赏
  • 举报
回复
哦,没注意Lz还是无限循环
这个做法有问题啊,每点一次按钮都会生成一个无限循环线程
所以,要么就是线程单次执行,不要无限循环,要么就是线程加个标志,按钮事件只使用一个线程,点击按钮时设置标志,线程收完信息改变标志,按钮处理查看标志来判断线程是否收完信息
飞跃颠峰 2011-07-13
  • 打赏
  • 举报
回复
他的ss用了while(true)且没有跳出机制,是执行不完的
qybao 2011-07-13
  • 打赏
  • 举报
回复
ss=new serverSocket(socket);//ss为serverSocket类的全局变量(已在前面定义过)
ss.start();
ss.join(); //要等待线程执行完才能判断结果
if(ss.isHaveReName==true)//通过检测isHaveReName值判断是否重名
飞跃颠峰 2011-07-13
  • 打赏
  • 举报
回复
ss是个线程,跟你输入昵称的主线程是同时执行的,
当ss.start()后,你不能假定一定是执行完ss.run()后才接着执行主线程后面的代码。
举例来说明此时你的代码的实际执行顺序:
ss.start() --> if(ss.isHaveReName==true)(主线程) --> if(serverInformation.equals("ReName")) isHaveReName=true(ss线程)

这样看你应该很清楚是什么问题了
bianhei000 2011-07-13
  • 打赏
  • 举报
回复
貌似你这个想法就有问题
不说你这个isHaveRename有没有必要 就算有的话该在客户端
皮皮 2011-07-13
  • 打赏
  • 举报
回复
问题就是在这几行
1。ss.start();

2。if(ss.isHaveReName==true)

因为ss 是线程,1和2是并列进行的。1还没有修改isHaveReName=true的时候 ,2都执行了。所以一直都是false
windyfzz 2011-07-13
  • 打赏
  • 举报
回复
你说的很对,“用Thread.sleep(xxx)的方式也不能完全保证每次都能正确的,因为线程的执行是不确定的,也就是说可能sleep很短时间就可以,可能sleep很长时间也不一定行,所以这个xxx你不好把握的,最好的方式还是设置标志

试了你的方法的确很不错 呵呵~ 不过用While(true)是必须的 因为当登陆成功之后就会产生一个客户端线程,这个线程必须要实时接收服务器的信息,所有要用While(true)一直监听。 而且只能登陆一次,并不要多次点击登陆按钮啊。只要登陆成功就会跳转到另一个界面,就像QQ登陆一样的。
windyfzz 2011-07-13
  • 打赏
  • 举报
回复
恩 是这样的。。。呵呵~ 我改了下 谢谢了啊!
yuanyue0540 2011-07-13
  • 打赏
  • 举报
回复
sleep只是暂时的。 它不能完全保证是正确的。

10楼写的挺好的,顶一下~
qybao 2011-07-13
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 windyfzz 的回复:]
恩 我懂了。 问题也解决了
ss=new serverSocket(socket);//ss为serverSocket类的全局变量(已在前面定义过)
ss.start();

Thread.sleep(xxx);//只要在这里让主线程睡眠会就可以了

if(ss.isHaveReName==true)//通过检测isHaveReName值判断是否重名
{
……
[/Quote]
你用Thread.sleep(xxx)的方式也不能完全保证每次都能正确的,因为线程的执行是不确定的,也就是说可能sleep很短时间就可以,可能sleep很长时间也不一定行,所以这个xxx你不好把握的,最好的方式还是设置标志
qybao 2011-07-13
  • 打赏
  • 举报
回复
ss=new serverSocket(socket);//ss为serverSocket类的全局变量(已在前面定义过)
你虽然定义为一个全局变量,但是你每次都是重新new一个,也就是说之前的那个还会存在,还会一直while循环,只是永远也不会收发消息了,即使socket关闭,你的try catch是在while里面,所以也不会退出while,这就是一个无限循环线程
而且你这段代码是每点击一次执行一次,所以就会有很多这种无意义的线程存在。
如果你希望只有一个线程,那么你也不必每次new一个socket,就用一个好了


//按钮事件
if (socket == null) { //socket是成员变量
socket=new Socket();
socket.connect(new InetSocketAddress(InetAddress.getByName(IP),sportNumber), 15*1000);
}
if(socket.isConnected())//如果连接服务器成功
{
if (ss == null) { //ss为成员变量,不必是全局
ss=new serverSocket(socket);//ss为serverSocket类的全局变量(已在前面定义过)
ss.start();
}
ss.setFlag(false); //设置收信标志
while (! ss.getFlag()) { //如果收信没结束,等待
Thread.sleep(100);
}
...

//线程
public class serverSocket extends Thread
{
boolean runnning = true; //结束标志
boolean flag = true; //收信标志
InputStream is=null;

BufferedReader br=null;

String serverInformation=null;

boolean isHaveReName=false;

Socket socket=null;

public serverSocket(Socket socket)
{
try
{
this.socket=socket;

is=socket.getInputStream();
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));

// os=socket.getOutputStream();
// ps=new PrintStream(os);
}
catch(Exception ex)
{
ex.printStackTrace();
}
}

public void run()
{
while(running) //这样好控制线程结束
{
try
{
serverInformation=getInformation();
flag = true; //接受完消息改变flag
if(serverInformation.equals("ReName"))
{
//System.out.println("收到重名信息!");
isHaveReName=true;

}
}
catch(Exception ex)
{
ex.printStackTrace();
}


}
}

public boolean getFlag() {return flag;}
public void setFlag(boolean b) {flag = b;}
public boolean isRunning() {return running;}
public void setRunning(boolean b) {running=b;}
windyfzz 2011-07-13
  • 打赏
  • 举报
回复
恩 我懂了。 问题也解决了
ss=new serverSocket(socket);//ss为serverSocket类的全局变量(已在前面定义过)
ss.start();

Thread.sleep(xxx);//只要在这里让主线程睡眠会就可以了

if(ss.isHaveReName==true)//通过检测isHaveReName值判断是否重名
{

JOptionPane.showMessageDialog(null, "你的昵称已被人使用!", "",
JOptionPane.WARNING_MESSAGE);
}


谢谢各位了! 呵呵
飞跃颠峰 2011-07-13
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 windyfzz 的回复:]

我实现的机制不是每点一次按钮都会生成一个无限循环线程
当登陆成功的时候 用while(true)客户端一直监听从服务器传来的消息。。。
这个线程只负责接受消息。。。
[/Quote]

原因大致就是这样,主要是对线程的执行理解有点偏差
当一个线程start后,它就脱离主线程独立运行了,你不能预期它的某条指令和主线程的某条指令的执行顺序。在这方面要做控制的话,就要用到线程类的其它方法了,你可以进一步地学习一下,根据实际需求灵活运用。
windyfzz 2011-07-13
  • 打赏
  • 举报
回复
我实现的机制不是每点一次按钮都会生成一个无限循环线程
当登陆成功的时候 用while(true)客户端一直监听从服务器传来的消息。。。
这个线程只负责接受消息。。。

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧