java中线程的状态获取

achengzz 2008-02-21 07:12:02
工作中需要获取java线程的状态。该线程与服务器进行通讯,阻塞读网络数据,当没有数据到达时,线程的状态应该是阻塞的,但是Thread.getState()返回是RUNNABLE的?因此,我就无法判断线程是否是阻塞的,请问这是为什么?是不是除Thread.getState()外还有别的获取线程状态的函数。

我做了一个示例程序
myserver.java 服务进程,接收客户发送的内容并将用户在控制台的输入信息发送给客户程序。

myclient.java客户进程,包含一个主线程和一个通讯线程(ClientThread.java)。
通讯线程将用户在控制台的输入信息发送给服务线程,并接收服务线程发送的信息(阻塞读)。
主线程创建通讯线程,并定期打印通讯线程的状态。

程序运行后,主线程打印的通讯线程的状态一直是RUNNABLE,而不管通讯线程是否正在阻塞读。因此,我就无法判断线程是否是阻塞的,请问这是为什么?是不是除Thread.getState()外还有别的获取线程状态的函数。


多谢!!!!


myserver.java服务进程
------------------------------------------------------------------------------------------
import java.net.*;
import java.io.*;

public class myserver
{
@SuppressWarnings("deprecation")
public static void main(String args[])
{
ServerSocket server;
Socket socket;
String s;
InputStream Is;
OutputStream Os;
DataInputStream DIS;
PrintStream PS;


try
{
//在端口4321注册服务
server=new ServerSocket(9999);
socket=server.accept();
//监听窗口,等待连接
System.out.println("server ok");
System.out.println("************************************************");
System.out.println("");
//获得对应Socket的输入/输出流
Is=socket.getInputStream();
Os=socket.getOutputStream();
//建立数据流
DIS=new DataInputStream(Is);
PS=new PrintStream(Os);
DataInputStream in=new DataInputStream(System.in);
while(true)
{
System.out.println("");
System.out.println("please wait client's message...");
System.out.println("");
s=DIS.readLine(); //读入从client传来的字符串
System.out.println("client said:"+s); //打印字符串
if(s.trim().equals("BYE"))
break; //如果是"BYE",就退出
System.out.print("you say:");
s=in.readLine(); //读取用户输入的字符串
PS.println(s); //将读取得字符串传给client
if(s.trim().equals("BYE"))
break; //如果是"BYE",就退出
}
//关闭连接
DIS.close(); //关闭数据输入流
PS.close(); //关闭数据输出流
Is.close(); //关闭输入流
Os.close(); //关闭输出流
socket.close(); //关闭sockey
}
catch(Exception e)
{
System.out.println("Error:"+e);
}
}
}



myclient.java客户进程
------------------------------------------------------------------------------------------
import java.net.*;
import java.io.*;
import java.lang.*;

public class myclient
{
public static void main(String args[]) throws InterruptedException
{
ClientThread ct = new ClientThread ();
Thread clientthread = new Thread(ct);
clientthread.start();
for(;;)
{
System.out.println(clientthread.getState());
Thread.sleep(2000);
}
}
}



ClientThread.java
------------------------------------------------------------------------------------------

import java.io.DataInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;


public class ClientThread implements Runnable
{
public void run()
{
Socket socket;
String s="yxfsoft@263.net";
String len;
InputStream Is;
OutputStream Os;
DataInputStream DIS;
PrintStream PS;

try
{
//向主机名为args[0]的服务器申请连接
//注意端口号要与服务器保持一致:4321
socket=new Socket("127.0.0.1",9999);


System.out.println("client ok");
System.out.println("************************************************");
System.out.println("");
//获得对应socket的输入/输出流
Is=socket.getInputStream();
Os=socket.getOutputStream();
//建立数据流
DIS=new DataInputStream(Is);
PS=new PrintStream(Os);
DataInputStream in=new DataInputStream(System.in);
while(true)
{
System.out.print("you say:");
s=in.readLine(); //读取用户输入的字符串
PS.println(s); //将读取得字符串传给server
if(s.trim().equals("BYE"))break; //如果是"BYE",就退出
else
{
System.out.println("");
System.out.println("please wait server's message...");
System.out.println("");
}
s=DIS.readLine(); //从服务器获得字符串
System.out.println("server said:"+s); //打印字符串
if(s.trim().equals("BYE"))break; //如果是"BYE",就退出
}
//关闭连接
DIS.close(); //关闭数据输入流
PS.close(); //关闭数据输出流
Is.close(); //关闭输入流
Os.close(); //关闭输出流
socket.close(); //关闭socket
}
catch(Exception e)
{
System.out.println("Error:"+e);
}
}
}
...全文
3149 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
wei2253498 2009-11-25
  • 打赏
  • 举报
回复
在进行多线程编程中,比较重要也是比较困难的一个操作就是如何获取线程中的信息。大多数人会采取比较常见的一种方法就是将线程中要返回的结果存储在一个字段中,然后再提供一个获取方法将这个字段的内容返回给该方法的调用者。如以下的ReturnThreadInfo类:

package threadtest1;

public class ReturnThreadInfo extends Thread {
private String str;

public ReturnThreadInfo() {
this.str = "Hello";
}

public void run(){
this.str = "Hello World!";
}

public String getThreadInfo(){
return this.str;
}
}
大家可以看到该类是一个线程类并含有一个初始值为"Hello"的字段str以及一个可以返回str值的方法:getThreadInfo(),而且当这个线程启动后str会被赋于新值:"Hello World!"。现在我想在另外一个类中启动ReturnThreadInfo线程,并通过getThreadInfo()方法获取值为"Hello World!"的变量并打印输出到控制台中。以下给出一个实现该功能的Main类:

package threadtest1;

public class Main{

public Main() {
}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
ReturnThreadInfo returnThreadInfo = new ReturnThreadInfo();
returnThreadInfo.start(); //创建并启动ReturnThreadInfo线程
System.out.println(returnThreadInfo.getThreadInfo()); //获取并输出returnThreadInfo对象的str的值
}

}
以上是一个多数熟悉单线程编程的人在第一反应下给出的实现方法。但是该类在运行的时候输出的结果却不是期望的"Hello World!"而是"Hello",这是由于线程的竞争条件导致的(由于ReturnThreadInfo线程和Main线程的优先级都为5,所以在很大几率上ReturnThreadInfo线程的run()方法还没有运行,Main类就已经运行System.out.println(returnThreadInfo.getThreadInfo());将"Hello"输出了。具体的原理可以参见另一篇文章:"java多线程的几点误区")。有的人可能会立即想到把ReturnThreadInfo线程的优先级设高些(比如最大的10)就可以returnThreadInfo线程的run()方法先运行完,然后Main类的System.out.println(returnThreadInfo.getThreadInfo())再运行,这样输出的结就一定是期望的"Hello World!"了。这种通过调整线程优先级的方法固然可以在某种程度上解决该问题,但是线程争用CPU运行时间的原理却决不仅仅只是优先级高低的原因(优先级高的线程并不意味着一定比优先级低的线程先运行,只是几率要更大一些)。你并不希望ReturnThreadInfo线程9999次都比Main先运行,却在最关键的一次在Main之后再运行。因此下面给出两种比较常见的获取线程信息的方法:
一、轮询
比较常见的一种解决方案是,让线程类获取方法在结果字段设置之前返回一个标志值。然后主线程定时询问获取方法,看是否返回了标志之外的值。以下给出了具体的实现方法,该方法不断测试str的值是否为"Hello",如果不为"Hello"才打印输出它。例如:

package threadtest1;

public class Main{

public Main() {
}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
ReturnThreadInfo returnThreadInfo = new ReturnThreadInfo();
returnThreadInfo.start(); //创建并启动ReturnThreadInfo线程

while(true){
String str = returnThreadInfo.getThreadInfo();
if(!str.equals("Hello")){
System.out.println(returnThreadInfo.getThreadInfo());
break;
}
}
}
}
这种方案虽然能起到作用,但是它做了大量不需要做的工作。事实上,还有一种更简单有效的方法来解决这个问题。

二、回调
轮询方法最大的特点是主类Main不断询问线程类是否结束,这实际上大量浪费了运行时间,特别是当线程特别多的时候。因此如果反过来在线程结束时,由线程自己告诉主类Main线程已经结束,然后Main再获取并输出str的值,这样就避免了轮询方法所带来的不必要的系统开销问题。
在具体的实现过程中,线程可以在结束时通过调用主类中的一个方法来实现告知功能,这种方法叫做回调。这样主类Main就可以在等待线程结束时休息,也就不会占用运行线程的时间。下面是修改后的Main类:
public class Main{

public Main() {
}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
ReturnThreadInfo returnThreadInfo = new ReturnThreadInfo();
returnThreadInfo.start();
}

public static void receiveStr(String str){
System.out.println(str);
}
}
相比于前面,我们在Main类中添加了一个静态方法receiveStr(String str),该方法是供线程结束之前调用,通过参数str将要返回的线程信息返回给Main类并输出显示出来。下面是修改后的ReturnThreadInfo类,该类在线程结束前回调了Main.receiveStr方法,通知线程已结束。

package threadtest1;
public class ReturnThreadInfo extends Thread {
private String str;

public ReturnThreadInfo() {
this.str = "Hello";
}

public void run(){
this.str = "Hello World!";
Main.receiveStr(str); //回调receiveStr方法
}
}

如果有很多个对象关心线程的返回的信息,线程可以保存一个回调对象列表。某个对象可以通过已经定义的一个对象将自己添加到列表中,表示自己对这些信息的关注。如果有多个类的实例关心这些信息,也可以定义一个interface,在interface中声名回调方法,然后这些类都实现这个接口。其实这是典型的java处理事件的方法,这么做可以使得回调更灵活,可以处理涉及更多线程、对象和类的情况。稍后会给出这种模仿事件处理模型的回调的实现方法。

转自:http://www.cn-java.com/www1/?action-viewnews-itemid-4527
superliyubo 2009-11-25
  • 打赏
  • 举报
回复
mark~~~~~~~
7761098 2008-05-19
  • 打赏
  • 举报
回复
但是如果线程都是同类同名的,怎么判断呢
千里冰封820 2008-02-23
  • 打赏
  • 举报
回复
设一个标量吧,在等待的时候就把这个标量设为true,表示正在等待,接受到了连接之后再把它设为false,然后在等待时再设为true就可以了
philo_xu 2008-02-23
  • 打赏
  • 举报
回复
好好的顶一个
achengzz 2008-02-23
  • 打赏
  • 举报
回复
to caiming250 lbfhappy
这里给出的是一个示例代码,实际环境中线程执行的是第三方代码(无法修改),只能通过线程句柄判读线程状态。
caiming250 2008-02-23
  • 打赏
  • 举报
回复
runnable 表示线程是可运行的 线程创建之后 到 死亡之前都是这个状态

看可以这样不:把你所有的线程都放置到一个HashMap<Thread,Boolean> 在线程调用阻塞之前修改Boolean这个值! 提供给别人查询!

achengzz 2008-02-22
  • 打赏
  • 举报
回复
TO 语文报
线程的阻塞是由网络读引起的。

TO xiaoyu_air
我给的示例代码。实际的情况下,需要判断线程的不同状态,自动进行相关处理。因此不能人工判断。
achengzz 2008-02-22
  • 打赏
  • 举报
回复
?
magic256 2008-02-21
  • 打赏
  • 举报
回复
不好意思,如果不能更改线程的代码,这个问题貌似很难解决了,希望有可以解决的高人出现
magic256 2008-02-21
  • 打赏
  • 举报
回复
你是不是没理解我的意思?我的意思是在ClientThread类中,你每进行一次阻塞操作,即readLine()方法的前后,加上我建议的那两句话
xiaoyu_air 2008-02-21
  • 打赏
  • 举报
回复
不一定非要用代码呀,
因为他是阻塞的,所以程序员完全可以知道什么情况下是否阻塞。
yuwenbao 2008-02-21
  • 打赏
  • 举报
回复
楼主,你用了sleep而没有用wait试试吗?
呵呵,你这样让我想起了一道基本面试题,sleep和wait的区别,sleep是不释放资源的也就是说你获取的状态runnable应该是对的,你用wait试试呢,然后再notify它,呵呵,只是思路,没有经过验证啊
achengzz 2008-02-21
  • 打赏
  • 举报
回复
to 年轻就是资本~~~~~~
工作中需要获取java线程的状态。这个线程是第三方代码,我无法修改。现在需要在外部根据线程的句柄判读该线程的状态,进行相关处理。
但是通过Thread.getState()获取的状态不正确。我做了试验,在线程阻塞读网络时,Thread.getState()返回的还是runnable。
magic256 2008-02-21
  • 打赏
  • 举报
回复
magic256 2008-02-21
  • 打赏
  • 举报
回复
厄。。。为什么没人跟帖了?
magic256 2008-02-21
  • 打赏
  • 举报
回复
如果客户端有很多你可以把这个信号量isblocked设置成公共非静态的放到每个客户端里面,
让服务器可以判断他是不是阻塞着,
至于为什么这两个操作要加synchronized,是为了防止线程不同步造成的腐蚀现象,即客户端在将阻塞信号量设为真后,还未来得及设为假(事实上是非阻塞的),就被服务器端先读取了isblocked,造成判断出错
magic256 2008-02-21
  • 打赏
  • 举报
回复
我是这样解决你的问题的
class Signal {
public static boolean isBlocked = false;
}



客户端里面这样写:
Signal sig = new Signal();
synchronized(sig) {
  Signal.isBlocked = true;
  String s = xxx.readline();
  Signal.isBlocked = false;
}

服务器端里面那个打印语句改成
Signal sig = new Signal();
synchronized(sig){
  System.out.println(Signal.isBlocked ? "客户端被阻塞":"客户端没被阻塞");
}
achengzz 2008-02-21
  • 打赏
  • 举报
回复
to 老紫竹

我只有一个CPU,并且我只需要判读通讯线程的状态。通讯线程阻塞读时,我已经打印了状态。
老紫竹 2008-02-21
  • 打赏
  • 举报
回复
程序太长,我就不看了!我给个思路吧!

你把所有已经启动的线程保存到一个Set/List里面
然后在需要的时候,循环判断他们的状态,不过一般来说,你当前的判断代码在运行,那些线程多数都在可运行状态,
除非多个CPU分配给了他们!

62,614

社区成员

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

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