`
神绮_H_亚里亚
  • 浏览: 9661 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

线程(中)

阅读更多

   线程的整个周期由线程创建、可运行状态、不可运行状态和退出等部分组成,这些状态之间的转化是通过线程提供的一些方法完成的。

1线程周期

 

      一个线程有4 种状态,任何一个线程都处于这4种状态中的一种状态
     
 1) 创建(new)状态:调用 new方法产生一个线程对象后、调用 start 方法前所处的状态。线程对象虽然已经创建,但还没有调用 start 方法启动,因此无法执行。当线程处于创建状态时,线程对象可以调用 start 方法进入启动状态,也可以调用 stop 方法进入停止状态。 
     
2)可运行(runnable)状态:当线程对象执行 start()方法后,线程就转到可运行状态。进入此状态只是说明线程对象具有了可以运行的条件,但线程并不一定处于运行状态。因为在单处理器系统中运行多线程程序时,一个时间点只有一个线程运行,系统通过调度机制实现宏观意义上的运行线程共享处理器。因此一个线程是否在运行,除了线程必须处于 Runnable 状态之外,还取决于优先级和调度。 
    
3)不可运行(non Runnable)状态:线程处于不可运行状态是由于线程被挂起或者发生阻塞,例如对一个线程调用 wait()函数后,它就可能进入阻塞状态;调用线程的notifynotifyAll 方法后它才能再次回到可执行状态。 
     
4)退出(done)状态:一个线程可以从任何一个状态中调用 stop 方法进入退出状态。线程一旦进入退出状态就不存在了,不能再返回到其他的状态。除此之外,如果线程执行完 run方法,也会自动进入退出状态。 
     
创建状态、可运行状态、不可运行状态、退出状态之间的转换关系如图所示。 


 通过 new第一次创建线程时,线程位于创建状态,这时不能运行线程,只能等待进一步的方法调用改变其状态。然后,线程通过调用 start方法启动
线程,并进入可执行状态,或者调用方法 stop进入退出状态。当程序位于退出状态时,线程已经结束执行,这是线程的最后一个状态,并且不能转化到其他状态。当程序的所有线程位于退出状态时,程序会强行终止。当线程位于可执行状态时,在一个特定的时间点上,每一个系统处理器只能运行一个线程。此时如果线程被挂起,执行就会被中断或者进入休眠状态,那么线程将进入不可执行状态,并且不可执行状态可以通过 resumenotify等方法返回到可执行状态。表10-1列举了线程状态转换的函数。 

     
  线程状态转换函数
方法                        描述                                                          有效状态             目的状态 
start()                    
开始执行一个线程                                     New                    Runnable 
stop()                    
结束执行一个线程                                     NewRunnable    Done 
sleep(long)          
暂停一段时间,这个时间为给定的毫秒  Runnable             NonRunnable 
sleep(long,int)    
暂停片刻,可以精确到纳秒                     Runnable             NonRunnable 
suspend()            
挂起执行                                                      Runnable             NonRunnable 
resume()              
恢复执行                                                      NonRunnable        Runnable 
yield()                    
明确放弃执行                                              Runnable             Runnable 
wait()                    
进入阻塞状态                                              Runnable             NonRunnable 
notify()                  
阻塞状态解除                                              NonRunnable       Runnable 
注意:stop()suspend() resume()方法现在已经不提倡使用,这些方法在虚拟机中可能引起死锁现象。suspend() resume()方法的替代方法是 wait() sleep()。线程的退出通常采用自然终止的方法,建议不要人工调用 stop()方法。 

2 线程的创建和启动 

      Java 是面向对象的程序设计语言,设计的重点就是类的设计与实现。Java 利用线程类Thread 来创建线程,线程的创建与普通类对象的创建操作相同。Java通过线程类的构造方法创建一个线程,并通过调用 start 方法启动该线程。 
       
实际上,启动线程的目的就是为了执行它的 run()方法,而 Thread 类中默认的 run()方法没有任何可操作代码,所以用 Thread类创建的线程不能完成任何任务。为了让创建的线程完成特定的任务,必须重新定义 run()方法。在第一节中已经讲述过,Java 通常有两种重新定义run()方法的方式:

      1)派生线程类 Thread 的子类,并在子类中重写 run()方法:Thread 子类的实例对象是一个线程对象,并且该线程有专门定制的线程 run()方法,启动线程后就执行子类中重写的 run()方法。 
‰    
  2)实现 Runnable 接口并重新定义 run()方法:先定义一个实现 Runnable()接口的类,在该类中定义 run()方法,然后创建新的线程类对象,并以该对象作为 Thread 类构造方法的参数创建一个线程。 
      
     
注意:调用线程的 run()方法是通过启动线程的start()方法来实现的。因为线程在调用start()方法之后,系统会自动调用 run()方法。与一般方法调用不同的地方在于一般方法调用另外一个方法后,必须等被调用的方法执行完毕才能返回,而线程的 start()方法被调用之后,系统会得知线程准备完毕并且可以执行run()方法,start()方法就返回了,start()方法不会等待run()方法执行完毕。
 

线程状态转换 

 

1.线程进入可执行状态 
   
当以下几种情况发生时,线程进入可执行状态。 
1)其他线程调用notify()或者 notifyAll()方法,唤起处于不可执行状态的线程。 
      public final void notify() 
      public final void notifyAll() 
     notify
仅仅唤醒一个线程并允许它获得锁,notifyAll 唤醒所有等待这个对象的线程,并允许它们获得锁。
2)线程调用 sleep(millis)方法,millis毫秒之后线程会进入可执行状态。 
         static void sleep(long millis) throws InterruptedException 
millis 毫秒数内让当前正在执行的线程进入休眠状态,等到时间过后,该线程会自动苏醒并继续执行。sleep方法的精确度受到系统计数器的影响。 
        static void sleep(long millis, int nanos) throws InterruptedException 
在毫秒数(millis)加纳秒数(nanos)内让当前正在执行的线程进入休眠状态,此操作的精确度也受到系统计数器的影响。 

 

3)线程对I/O操作的完成。 

 

2.线程进入不可执行状态 
   
当以下几种情况发生时,线程进入不可执行状态。 
1)线程自动调用 wait()方法,等待某种条件的发生。 
      public final void wait() throws InterruptedException 
     
当其他线程调用 notify()方法或 notifyAl()方法后,处于等待状态的线程获得锁之后才会被唤醒,然后该线程一直等待重新获得对象锁才继续运行。 
2)线程调用 sleep()方法进入不可执行状态,在一定时间后会进入可执行状态。 
3)线程等待 I/O操作的完成。 

 

   线程阻塞的例子。 

 

public class  ThreadSleep   
{   
 public static void main(String[ ] args)    
 {   
  SubThread st = new SubThread("SubThread");  //创建,并初始化SubThread 对象st   
  st.start();         //启动线程st   
 }   
}   
   
	class SubThread extends Thread{   
	 SubThread(){}        //声明,实现SubThread无参数构造方法   
	 //声明,实现SubThread带字符串参数构造方法   
	 SubThread(String Name)   
	 {   
	  super(Name);        //调用父类的构造方法   
	 }   
	 //重载run函数   
	 public void run()   
	 {   
	  for (int count = 1,row = 1; row < 10; row++,count++) //循环计算输出的*数目   
	  {   
	   for (int i = 0; i < count; i++)      //循环输出指定的count数目的*   
	   {   
	    System.out.print('*');     //输出*   
	   }   
	   try         //try-catch块,用于捕获异常   
	   {     Thread.sleep(1000);     //线程休眠1秒钟   
	    System.out.print("\t wait........");   
	   }   
	   catch (InterruptedException e)    //捕获异常InterruptedException   
	   {   
	    e.printStackTrace();     //异常抛出信息   
	   }   
	   System.out.println();      //输出换行符   
	  }   
	 }   
	}   

     程序ThreadSleep 中,每输出一行*就要休息1 秒钟。当执行 sleep()语句后,线程进入不可执行状态等待1 秒钟之后,线程 st 会自动苏醒并继续执行。由于 sleep

 

 

方法抛出 InterruptedException异常,所以在调用时必须捕获异常。 

 

4 等待线程结束 

 

     isAlive()方法用来判断一个线程是否存活。当线程处于可执行状态或不可执行状态时,isAlive()方法返回 true当线程处于创建状态或退出状态时,则返回 false也就是说, isAlive()方法如果返回 true,并不能判断线程是处于可运行状态还是不可运行状态。isAlive()方法的原型如下所示。 
      public final boolean isAlive() 

 

      该方法用于测试线程是否处于活动状态。活动状态是指线程已经启动(调用 start方法)且尚未退出所处的状态,包括可运行状态和不可运行状态。可以通过该方法解决程序 10.3中的问题,先判断第一个线程是否已经终止,如果终止再来调用第二个线程。这里提供两种方法:

 

      第一种方法是不断查询第一个线程是否已经终止,如果没有,则让主线程睡眠一直到它终止即“while/isAlive/sleep”,格式如下。 

 

<!--[if !supportLists]-->1.     <!--[endif]-->线程1.start();   

 

<!--[if !supportLists]-->2.     <!--[endif]-->while(线程 1.isAlive()) {   

 

<!--[if !supportLists]-->3.     <!--[endif]--> Thread.sleep(休眠时间);   

 

<!--[if !supportLists]-->4.     <!--[endif]-->}   

 

<!--[if !supportLists]-->5.     <!--[endif]-->线程2.start();   

    第二种是利用 join()方法。 
    1)public final void join(long millis) throws InterruptedException 
等待该线程终止的时间最长为毫秒(millis),超时为0 意味着要一直等下去。 
    2) public final void join(long millis,int nanos) throws InterruptedException 
等待该线程终止的时间最长为毫秒(millis)加纳秒(nanos)。  
    3)public final void join() throws InterruptedException 
等待该线程终止。 
   
等待线程结束并执行另外一个线程的例子。   该例子 等待一个线程的结束的两种方法  :

 

package Test;  
  
class WaitThreadStop extends Thread {  
    // 声明,并实现WaitThreadStop无参数构造方法  
    WaitThreadStop() {  
    }  
  
    // 声明,并实现带有一个字符串参数的构造方法  
    WaitThreadStop(String szName) {  
	        super(szName); // 调用父类的构造方法  
	    }  
	  
	    // 重载run函数  
	    public void run() {  
	        for (int count = 1, row = 1; row < 10; row++, count++) {  
	            for (int i = 0; i < count; i++) {  
	                System.out.print('*'); // 输出*  
	            }  
	            System.out.println(); // 输出换行符  
	        }  
	    }  
	}  
	  
	public class WaitThreadStopMain {  
	    public static void main(String argv[ ]){   
	      WaitThreadStopMain test = new WaitThreadStopMain();    //创建,初始化WaitThreadStopMain对象test   
	      test.Method1();  //调用Method1方法   
	      //test.Method2();   
	     }  
	    // 第一种方法:while/isAlive/sleep  
	    public void Method1() {  
	        WaitThreadStop th1 = new WaitThreadStop(); // 创建,并初始化WaitThreadStop对象th1  
	        WaitThreadStop th2 = new WaitThreadStop(); // 创建,并初始化WaitThreadStop对象th2  
	        // 执行第一个线程  
	        th1.start();  
	        // 查询第一个线程的状态  
	        while (th1.isAlive()) {  
	            try {  
	                Thread.sleep(100); // 休眠100毫秒  
	            } catch (InterruptedException e) {  
	                e.printStackTrace(); // 异常信息输出  
	            }  
	        }  
	        // 当第一个线程终止后,运行第二个线程  
	        th2.start(); // 启动线程th2  
	    }  
	  
	    // 第二种方法,使用join方法实现等待其他线程结束  
	    public void Method2() {  
	        WaitThreadStop th1 = new WaitThreadStop(); // 创建, 并初始化WaitThreadStop对象th1  
	        WaitThreadStop th2 = new WaitThreadStop(); // 创建,并初始化WaitThreadStop对象th2  
	        // 执行第一个线程  
	        th1.start();  
	        try {  
	            th1.join(); // th1调用join 方法  
	        } catch (InterruptedException e) {  
	            e.printStackTrace(); // 异常信息输出  
	        }  
	        // 执行第二个线程  
	        th2.start();  
	    }  
	}  

   多线程应用程序的每一个线程的重要性和优先级可能不同,例如有多个线程都在等待获得CPU的时间片,那么优先级高的线程就能抢占CPU并得以执行;当多个线程交替抢占CPU时,优先级高的线程占用的时间应该多。因此,高优先级的线程执行的效率会高些,执行速度也会快些。 

 

 

        Java 中,CPU的使用通常是抢占式调度模式不需要时间片分配进程。抢占式调度模式是指许多线程同时处于可运行状态,但只有一个线程正在运行。当线程一直运行直到结束,或者进入不可运行状态,或者具有更高优先级的线程变为可运行状态,它将会让出 CPU。线程与优先级相关的方法如下:

 

      public final void setPriority(int newPriority) 设置线程的优先级为 newPriority           

 

      newPriority 的值必须在 MIN_PRIORITY MAX_PRIORITY范围内,通常它们的值分别是110。目前Windows系统只支持3个级别的优

 

先级,它们分别是Thread.MAX_PRIORITY Thread.MIN_PRIORITYThread.NORM_PRIORITY  

 

      public final int getPriority() 获得当前线程的优先级。

 

     线程优先级的例子:

 

 

class  InheritThread extends Thread {   
    //自定义线程的run()方法   
    public void run(){   
         System.out.println("InheritThread is running…"); //输出字符串信息   
         for(int i=0;i<10;i++){   
              System.out.println(" InheritThread: i="+i);  //输出信息   
              try{   
                  Thread.sleep((int)Math.random()*1000); //线程休眠   
             }   
	             catch(InterruptedException e)     //捕获异常   
	             {}   
	        }   
	    }   
	}   
    //通过Runnable接口创建的另外一个线程 :
class RunnableThread implements Runnable {  
    // 自定义线程的run()方法  
    public void run() {  
        System.out.println("RunnableThread is running…"); // 输出字符串信息  
        for (int i = 0; i < 10; i++) {  
            System.out.println("RunnableThread : i=" + i); // 输出i  
            try {  
                Thread.sleep((int) Math.random() * 1000); // 线程休眠  
            } catch (InterruptedException e) { // 捕获异常  
	            }  
	        }  
	    }  
	}  
	  
	public class ThreadPriority {  
	    public static void main(String args[]) {  
	        // 用Thread类的子类创建线程  
	        InheritThread itd = new InheritThread();  
	        // 用Runnable接口类的对象创建线程  
	        Thread rtd = new Thread(new RunnableThread());  
	        itd.setPriority(5); // 设置myThread1的优先级5  
	        rtd.setPriority(5); // 设置myThread2的优先级5  
	        itd.start(); // 启动线程itd  
	        rtd.start(); // 启动线程rtd  
	    }  
	}  

 在程序ThreadPriority.java中,线程 rtd itd 具有相同的优先级,所以它们交互占用 CPU,宏观上处于并行运行状态。结果如图3. 

 

 

重新设定优先级: 

itd.setPriority(1);  //设置myThread1的优先级
rtd.setPriority(10); //
设置myThread2的优先级10 
运行程序结果如图所示。



相同优先级


不同优先级
 
 

 从运行结构可以看出程序ThreadPriority.java修改后,由于设置了线程itd rtd 的优先级,并且 rtd的优先级较高,基本上是 rtd都优先抢占 CPU资源。 



 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics