您的位置:首页 > 编程语言 > Java开发

多线程基础知识整理(一)

2009-05-31 22:07 232 查看
CS聊天室程序相关:
通过最近做的这个聊天程序,可以比较直观的巩固多线程编程的思路。
在最近这个刚刚接触java的阶段,我试图通过一个很简单的聊天室程序巩固并且跟进j2se的一些语言基础知识。
这个实验的主要代码由客户端类和服务器类两部分组成。多线程是为了使每一个用户打开一个聊天界面时都能启动服务器的线程,从而进行多人聊天。
在这个例子中,如果不用多线程而是用传统方法,那么整个聊天室只能有一个用户,当第二个用户登录时,它将无法占用已被第一个用户所占用的Socket。也就是说,每当一个用户打开聊天室,就相当于启动了一个新的线程,也代表创建了一个新的Socket。
由此可见,多线程编程是java中非常重要的一块。因为很多应用程序都需要这种互不干扰的机制。
 
多线程笔记整理一:
参考书:java完全自学手册——林树泽等编著
          Java,Java,Java Object-Oriented Problem Solving, Second Edition ——Ralph Morelli
关于线程, Ralph Morelli写的java OOP Solving中写的非常形象
“例如,假设你今天的早餐有麦片粥、烤面包和一杯咖啡。于是,吃早餐时你在同一时间内要做三件事情:喝麦片粥、吃面包以及喝咖啡。所谓的同一时间内做这些事情是指在它们之间来回切换:喝了一勺麦片粥,然后咬了一口面包片,接着喝一口咖啡………我们所编写的计算机程序(在非常初级的阶段)在同一时间内只完成了一项任务。而大量的应用程序需要在同一时间处理多个任务……”这个,就是线程解决的问题。“同一时间内做了多个动作”。
 
1、继承Thread类创建新线程的基本步骤:
 
a. 创建新线程类:继承Thread类。以我的这个实验为例,为实现多个客户端通讯,每个客户端都会启动服务器端的这个线程:class ClientThread extends Thread{……}。注意以后要实现的是服务器接收客户的聊天内容再把它们广播出去,所以这个线程写在服务器端,属于服务器类的内部类。
 
b. 构造方法:如果新的线程需要初始化一些参数,就要写构造方法。同样以本次实验为例,在实现客户端和服务器间通信时首先要用到的就是Socket。因此需要为线程提供参数Socket s:public ClientThread(Socket s){this.s=s;……(根据程序需要,构造方法中还会添加其他代码)}
 
c.重写run()方法:run()方法是线程的核心,所有的主要执行代码都会被写在run()里面,没有参数。以此次实验为例,线程主要做的是接收客户端提供的聊天内容,之后广播给所有客户端。所以这个程序中的run主要是输入输出这些消息等内容。
 
d.启动线程:调用start()启动线程。如果忘记写这句话,那整个线程永远也不会被调用了。
在这个实验中在服务器类的构造方法中启动了这个线程,对象为装载所有客户新Socket的容器对象,关于容器先不作解释。
 
2、关于Runnable接口
在我的程序中我没有用到Runnable接口。但听说这是一个非常重要的接口。原因是java不支持多继承,当某个类已经继承了另外一个类,但又要实现多线程机制时,显然就不能用extends Tread这样的句子了。Runnable接口解决了这个问题。
例如上述实验如果改成接口去写,那创建线程的代码可以是 public class ClientThread extends (被继承的某个类)implement Runable{……}
 
3、线程的状态
这个好像以前学OS的时候就已经涉及了。主要有五个状态:新建、就绪、运行、阻塞。
a. 新建状态:其实也就是创建线程对象。很显然,在写了一个内部类以后,必须在程序入口建对象才可以被真正使用。这个实验中,主方法里new了这个线程类的对象:new ChartServer();
b. 就绪状态:就绪状态就是调用start()方法以后,线程所处的状态。
c.运行状态:CPU开始执行run()中的代码了,自然就是运行状态了。
d. 阻塞状态:需要满足一定条件后才可以运行。
这里会用到sleep()方法,sleep(100)翻译成人话就是让线程睡100个时间单位再继续运行。想象一下如果你要做个计时之类的程序,你希望它一秒打出一个新的时间,最好加一个sleep(1),让它走的慢一点。
还会用到的是wait()和notify()或者notifyAll方法。wait()方法和sleep()的功能一样,不同的是调用wait()方法时,这个线程块会被释放,其他对象可以调用这一块的方法,而sleep()是线程“睡了”但“地方”没有让出来。使用wait方法还要用notify()和notifyAll()讲线程唤醒继续执行,sleep()没有这个必要。这两个方法通过字面就可以知道,notify()是唤醒一个线程而notifyAll()是唤醒全部。
注意:susupend()方法已经不使用了。
e. 死亡状态:线程退出即死亡状态。因为stop()或destroy()都已经不使用了。因此通过其他方法来退出。在这个实验中,使用了死循环的方法。并且设置一个boolean类型的参数。其实这里我也不特别理解,但看到很多书上都用这个方法。这里贴出我实验里的代码,逻辑代码先忽略。主要看里面的while循环以及利用定义isConnect这个参数的这个方法:
boolean isConnected = true;
  public void run() {
   try {
    while (isConnected) {   
     msg=dis.readUTF();
     System.out.println(msg);
     for(int i=0;i<clients.size();i++){
      ClientThread c=(ClientThread) clients.get(i);
      c.SendMsg(msg);
    }
  ……
}
 
 
待续
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息