知ing

Java语言程序设计(第三版)

邵丽萍,邵光亚,张后扬 编 / 清华大学出版社

拼命菇凉@ 上传

查看本书

10.1 多线程的概念
10.1.1 程序、进程和多任务
  程序:是对数据描述与操作的代码的集合,是应用程序执行的脚本
  进程:是程序的一次执行过程,程序是静态的,进程是动态的。系统运行一个程序就是一个进程从创建、运行到消亡的过程。
  多任务:一个系统中可以同时运行多个程序。一个CPU同时只能执行一个程序的一条指令,多任务运行的并发机制使这些任务交替运行。
10.1.2 线程
  线程也称为轻型进程(LWP),是比进程更小的运行单位,一个进程可以被划分成多个线程。当一个程序执行多线程时,可以运行两个或多个由同一个程序启动的任务。这样一个程序可以使得多个活动任务同时发生。
10.1.3 多线程
  与进程不同的是,同类多线程共享一块内存空间和一组系统资源,所以系统创建多线程开销相对较小。
10.1.4 线程的生命同期与Java的多线程机制
  1.线程的生命同期与状态
  线程有创建(New)、可运行(Runnable)、运行中(Running)、挂起(Not Runnable)、死亡(Dead)5种状态。
  2.Java的多线程机制
  java.lang中的线程类Thread封装了所有需要的线程操作控制,有很多方法用来控制一个线程的运行、休眠、挂起或停止。
10.2 创建线程
  建立一个线程需要完成三件事:
> 建立一个虚拟的CPU
> 给出线程的执行代码
> 提供代码所操作的数据
  两种方法可以创建线程:一种方法是通过继承线程类Thread来创建线程类;另一种方法是建立一个实现Runnable接口的类一创建线程 。
10.2.1 通过继承Thread类创建线程
  继承Thread类这种方法中,需要覆盖run( )方法来提供线程的执行代码,定义Thread类的成员变量来提供线程的数据。线程执行时,从它的run( )方法中开始执行,run()方法是线程执行的起点
  例:public class SC extends Thread{
    int count=1,number;
    SC(int num){
    number=num;
    System.out.println("创建线程"+number);  }
    public void run(){
      while(true){
        System.out.println("线程"+number+":计数"+count);
        if(++count==3)   return;   }  }
    public static void main(String args[]){
     for (int i=0;i<3;i++)
        new SC(i+1).start();  } }
运行结果:创建线程1
     创建线程2
     创建线程3
     线程1:计数1
     线程1:计数2
     线程2:计数1
     线程2:计数2
     线程3:计数1
     线程3:计数2
例:P208
 public class SC extends Thread{
   public static void main(String args[]){
     testThread t1=new testThread("线程1");//仅是一空线程对象,没有启动这一线程,系统不为它分配资源,
     testThread t2=new testThread("线程2");
     t1.start();   t2.start(); } } //启动线程
 class testThread extends Thread{
  public testThread(String str)
   {super(str);} //调用父类构造方法为线程对象命名
  public void run(){
     for (int i=0;i<3;i++){
     System.out.println(getName()+"在运行"); //getName返回线程名称
     try
      {sleep(1000); //用休眠1000毫秒来区分哪个线程在运行
      System.out.println(getName()+"在休眠");}
    catch(InterruptedException e){} }
  System.out.println(getName()+"已结束");  }}
说明:由继承Thread创建的子类,必须覆盖run方法,因为run方法是abstact抽象方法
10.2.2 通过Runnable接口创建线程
  如果是Applet类应不能再继承Thread类(不能多继承),这时可以通过接口Runnable直接创建线程对象。接口中只声明了一个未实现的run方法。
线程体的构造方法:
public Thread([ThreadGroup group][,Runnable target][,String name])
其中group指明该线程所属的线程组,target是执行线程体的目标对象,它必须实现接口Runnable,name则为线程名。这三个参数均可任意没有。
任何实现接口Runnable的对象都可以作为一个线程的目标对象,类Thread本身也实现了接口Runnable,因此我们可以通过两种方法实现线程体。
(1)定义一个线程类,它继承类Thread并重写其中的方法run(),这时在初始化这个类的实例时,目标对象target可为null
(2)提供一个实现接口Runnable类作为线程的目标对象,在初始化一个Thread类或者Thread子类的对象时,把目标对象传递给这个线程实例,由该目标对象提供线程体run()。这时实现接口Runnable的类仍然要以继承其他父类。
例:P210
  import java.awt.*;
  import java.applet.Applet;
  import java.util.*;
  import java.text.DateFormat;
  public class SC extends Applet implements Runnable{
   Thread clockThread=null;
   public void init(){
    setBackground(Color.blue);
    setForeground(Color.yellow); }
   public void start(){
     if(clockThread==null){
       clockThread=new Thread(this,"Clock2");
    //Thread(ThreadGroup group,String name)name是线程名,group指定线程所属的组
       clockThread.start();  } }
    public void run(){
     Thread myThread=Thread.currentThread();
   //找到当前执行的线程,返回它的引用
     while(clockThread==myThread){
        repaint();
        try{ Thread.sleep(1000);
     }  catch(InterruptedException e){}
   } }
  
  public void paint(Graphics g){
    Date date=new Date();
    DateFormat formatter=DateFormat.getTimeInstance();
    String str=formatter.format(date);
    g.drawString(str,5,10); }
   public void stop()
    {clockThread=null;} }
10.2.3 可运行状态(Runnable)
  Thread MyThread=new Thread()
  MyThread.start();
  这样该线程处于可运行(Runnable)状态,注意,这状态并不是运行中状态(Running),因为线程也许实际并未真正运行(因很多计算机是单CPU)。当一个线程正在运行时,它是可运行的,并也是当前正运行的线程
10.2.4 不可运行状态(Not Runnable)
  当下面四种情况发生时,线程就是进入不可运行状态:
(1)调用了sleep()方法
(2)调用了suspend()方法
(3)为等候一个条件变量,线程调用wait()方法
(4)输入输出流中发生线程阻塞
我们来看看下面这个例子:
  Thread myThread=new Thread();
  MyThread.start();
  try{   myThread.sleep(10000);}
  catch(InterrupedException e){
  }
  对于上面四种情况,都有特定的返回可运行状态的方法与之对应,对应方法如下:
  (1)如果线程处于睡眠状态中,sleep()方法中的参数为休息时间,当时间过去后,线程即为可运行的。
  (2)如果一线程被挂起,须由其他线程调用resume()方法来恢复该线程的执行。
  (3)如果线程在等待条件变量,那么要停止等待的话,要该条件变量所在的对象调用notify(),notifyAll()方法。
  (4)如果在I/O流中发生线程阻塞,则特定的I/O指令将结束这种不可运行状态
10.2.5 死亡状态(Dead)
  一般可通过两种方法实现:自然撤消或是被停止
  1.自然撤消
  public void run(){
     int i=0;
     while(i<100){
     i++;
     System.out.println(“i=”+i);  }}
 当run()方法结束后,该线程应自然撤消了
  2.被停止
   Thread myThread=new Thread();
     MyThread.start();
   try{Trhead.currentThread().sleep(10000);
   } catch(InterruptedException e)
   myThread.stop();
10.2.6 非法状态处理(IllegalThreadStateException)
  当一个线程刚被创建后,只能对它调用start()或stop()方法,若调用其他方法则会引起非法状态处理。同样对于任何状态,如果所调用的方法与状态不符,都会引起非法状态处理。
10.3  线程的优先级
  Java为使有些线程可以提前得到服务,可给线程设置优先级。在单个CPU上运行多线程时采用了线程队列技术,Java虚拟机支持固定优先级队列,一个线程的执行顺序取决于其对其他Runnable线程的优先级。优先级分1~10同,默认级别是5级。
Thread的公用静态常量表示:
  public static final int MAX_PRORITY=10 (最大优先级10)
  public static final int MIN_PRORITY=1 (最小优先级10)
  public static final int NORM_PRORITY=5 (默认优先级5)
  
  public final int getPriority( ) //获得线程优先级
  public final setPriority(int newPriority) //设置线程优先级
10.4  线程的调度与控制
10.4.1 线程类的方法
  1.线程的类方法
  Thread类的静态方法,即可直接从Thread类调用
  CurrentThread( ):返回正在运行的Thread对象名称
  sleep(int n):让当前线程休眠n毫秒
  2.实例方法P213
10.4.2 控制线程的状态
  1.挂起一个线程:suspend( )方法
  t.suspend():将t暂停执行,必须由其他线程调用t.resume()恢复,不提倡使用该方法,因为容易造成死锁
  2.停止一个线程:stop( )方法
  当线程完成运行并结束后,将不能再运行。另不可用t.stop()强行终止线程。注:这并没有消灭这个线程,只是停止线程的执行。但这个线程不能用t.start()重新启动。不提倡采用这种方法,易造成线程的不一致。要通过设置flag通知一个线程应该结束。
  3.线程休眠:sleep(long )方法
  Thread.sleep()使一个线程暂停运行一段固定时间。
  4.连接线程:join( )方法
  t.join()使当前的线程等待,直到t结束为止,线程恢复到可运行状态。三种调用格式:
  (1)join():如当前线程发出调用t.join(),则当前线程将等待线程t结束后再继续执行。
  (2)join(long millis):如当前线程发出调用t.join(long millis),则当前线程将等待线程t结束或最多等待mills毫秒后再继续执行。
  (3)join(long millis,int nanos):如当前线程发出调用t.join(long millis,int nanos),则当前线程将等待线程t结束或最多等待mills毫秒+nanos纳秒后再继续执行。
  5.暂停(退让)线程:yield( )方法(只让给同优先级运行)
  调用t.yield()方法可暂停当前运行线程,但处于可运行状态,让同优先级线程先运行。若没有同等优先级的线程是可运行状态,yield方法将什么也不做。
  6.中断线程:interrupt( )方法
  如果一个线程t在调用sleep(),join(),wait()方法被阻塞时,则t.interrupt()方法将使t的中断状态被清除,中断t的阻塞状态,并且将接收到InterruptException异常。
  一个线程可以通过获取另一个线程的引用,调用该线程的interrupt()方法来中断另一个sleep或wait线程的执行
  7.了解线程的状态:isAlive( )方法
  返回true则线程处于Runnable或Not Runnable状态(即已启动但还没有运行结束),返回false则说明线程处于New Thread或Dead状态
  
   
    start()         调度
     
                    时间片到yield()             run(),stop()结束
  
        时间到     sleep()
        interrupt()  join()       wait()
              synchronized()
                 获得互斥
                   使用权       
  
              notify()
             notifyall()
           线程状态的转换图
  
  例:public class SC{
  public static void main(String args[]) throws Exception{
    int i=0;
    Hello t=new Hello();
    t.start();
    while(true){
      System.out.println("早上好!"+i++);
      if (i==2&&t.isAlive()){
          System.out.println("Main waiting for Hello!");
          t.join(); //等待t运行结束 
    }
      if (i==4) break;   }}}
  class Hello extends Thread{
    int i;
    public void run(){
     while (true){
     System.out.println("嗨"+i++);
     if(i==4) break; } } }
  运行结果:
    早上好!0
    早上好!1
    Main waiting for Hello!
    嗨0
    嗨1
    嗨2
    嗨3
    早上好!2
    早上好!3
10.5  线程的同步机制与共享资源(自学)

查看更多