java线程进阶

一、线程的生命周期
      这里所说的线程的生命周期,也是根据Thread类里面的方法来定义的。JDK API 1.6里和生命周期有关的方法有一些几个:
      1、interrupt():中断线程。
      2、interrupted():测试当前线程是否已经中断。
      3、isInterrupted():测试线程是否已经中断。
      4、join():等待该线程终止。
      5、join(long millis):等待该线程终止的时间最长为 millis 毫秒。
      6、join(long millis, int nanos):等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
      7、run():如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
      8、sleep(long millis, int nanos): 在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
      9、start(): 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
      10、yield():暂停当前正在执行的线程对象,并执行其他线程。
      11、isAlive(): 测试线程是否处于活动状态。
      对于官方已经弃用不建议使用的方法没有列举,从这些方法中可以看出,目前的java线程生命周期有开始运行、睡眠、等待、中断、暂停,但是没有了重新开始。Java的线程是不能重启的,也就是说,当线程的run()方法执行到最后一行,退出之后,这个线程就结束了,不能再通过start()方法重启启动这个线程,只能重新构造一个线程对象,再调用其start()方法来启动,但这个对象和原来那个对象已经不同了。

     线程在建立后并不马上执行run方法中的代码,而是处于等待状态。线程处于等待状态时,可以通过Thread类的方法来设置线程不各种属性,如线程的优先级(setPriority)、线程名(setName)和线程的类型(setDaemon)等。当调用start方法后,线程开始执行run方法中的代码。线程进入运行状态。可以通过Thread类的isAlive方法来判断线程是否处于运行状态。当线程处于运行状态时,isAlive返回true,当isAlive返回false时,可能线程处于等待状态,也可能处于停止状态。
[java] view plain copy print ?
  1. <SPAN style="FONT-FAMILY: SimSun">public class LifeCycle extends Thread    
  2. {    
  3.     public void run()    
  4.     {    
  5.         int n = 0;    
  6.         while ((++n) < 1000);            
  7.     }    
  8.          
  9.     public static void main(String[] args) throws Exception    
  10.     {    
  11.         LifeCycle thread1 = new LifeCycle();    
  12.         System.out.println("isAlive: " + thread1.isAlive());    
  13.         thread1.start();    
  14.         System.out.println("isAlive: " + thread1.isAlive());    
  15.         thread1.join();  // 等线程thread1结束后再继续执行      
  16.         System.out.println("thread1已经结束!");    
  17.         System.out.println("isAlive: " + thread1.isAlive());    
  18.     }    
  19. }  </SPAN>  
要注意一下,在上面的代码中使用了join方法,这个方法的主要功能是保证线程的run方法完成后程序才继续运行,上面代码的运行结果:

isAlive: false
isAlive: true
thread1已经结束!
isAlive: false

  一但线程开始执行run方法,就会一直到这个run方法执行完成这个线程才退出。但在线程执行的过程中,可以通过两个方法使线程暂时停止执行。这两个方法是yield和sleep。thread.yield()在多线程程序中,为了防止某线程独占CPU资源(这样其它的线程就得不到"响应"了).可以让当前执行的线程"休息"一下.但是这种thread.yield() 调用,并不保证下一个运行的线程就一定不是该线程.而使用sleep使线程休眠后,只能在设定的时间后使线程处于就绪状态(在线程休眠结束后,线程不一定会马上执行,只是进入了就绪状态,等待着系统进行调度)。在使用sleep时要注意,不能在一个线程中来休眠另一个线程。如main方法中使用thread.sleep(2000)方法是无法使thread线程休眠2秒的,而只能使主线程休眠2秒。在使用sleep方法时有四点需要注意:

1. sleep方法有两个重载形式,其中一个重载形式不仅可以设毫秒,而且还可以设纳秒(1,000,000纳秒等于1毫秒)。但大多数操作系统平台上的Java虚拟机都无法精确到纳秒,因此,如果对sleep设置了纳秒,Java虚拟机将取最接近这个值的毫秒。

2. 在使用sleep方法时必须使用throws或try{...}catch{...}。因为run方法无法使用throws,所以只能使用try{...}catch{...}。当在线程休眠的过程中,使用interrupt方法(这个方法将在2.3.3中讨论)中断线程时sleep会抛出一个InterruptedException异常。

3. sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。

4. sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会。

[java] view plain copy print ?
  1. <SPAN style="FONT-FAMILY: SimSun">class TestThreadMethod extends Thread{    
  2. public static int shareVar = 0;    
  3. public TestThreadMethod(String name){    
  4. super(name);    
  5. }    
  6. public void run(){    
  7. for(int i=0; i<4; i++){    
  8. System.out.print(Thread.currentThread().getName());    
  9. System.out.println(" : " + i);    
  10. //Thread.yield(); (1)     
  11. /* (2) */    
  12. try{    
  13. Thread.sleep(3000);    
  14. }    
  15. catch(InterruptedException e){    
  16. System.out.println("Interrupted");    
  17. }}}    
  18. }    
  19. public class TestThread{    
  20. public static void main(String[] args){    
  21. TestThreadMethod t1 = new TestThreadMethod("t1");    
  22. TestThreadMethod t2 = new TestThreadMethod("t2");    
  23. t1.setPriority(Thread.MAX_PRIORITY);    
  24. t2.setPriority(Thread.MIN_PRIORITY);    
  25. t1.start();    
  26. t2.start();    
  27. }    
  28. } </SPAN>  

运行结果为:

    
    
    
    
  1. t1 : 0  
  2. t1 : 1  
  3. t2 : 0  
  4. t1 : 2  
  5. t2 : 1  
  6. t1 : 3  
  7. t2 : 2  
  8. t2 : 3 

由结果可见,通过sleep()可使优先级较低的线程有执行的机会。注释掉代码(2),并去掉代码(1)的注释,结果为:

    
    
    
    
  1. t1 : 0  
  2. t1 : 1  
  3. t1 : 2  
  4. t1 : 3  
  5. t2 : 0  
  6. t2 : 1  
  7. t2 : 2  
  8. t2 : 3 

可见,调用yield(),不同优先级的线程永远不会得到执行机会。

有二种方法可以使终止线程。

1.  使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

2.  使用interrupt方法中断线程。

1. 使用退出标志终止线程

当run方法执行完后,线程就会退出。但有时run方法是永远不会结束的。如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如while循环。如果想让循环永远运行下去,可以使用while(true){...}来处理。但要想使while循环在某一特定条件下退出,最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while循环是否退出。下面给出了一个利用退出标志终止线程的例子。

[java] view plain copy print ?
  1. <SPAN style="FONT-FAMILY: SimSun">public class ThreadFlag extends Thread    
  2. {    
  3.     public volatile boolean exit = false;    
  4.    
  5.     public void run()    
  6.     {    
  7.         while (!exit);    
  8.     }    
  9.     public static void main(String[] args) throws Exception    
  10.     {    
  11.         ThreadFlag thread = new ThreadFlag();    
  12.         thread.start();    
  13.         sleep(5000); // 主线程延迟5秒     
  14.         thread.exit = true;  // 终止线程thread     
  15.         thread.join();    
  16.         System.out.println("线程退出!");    
  17.     }    
  18. }  </SPAN>  

在上面代码中定义了一个退出标志exit,当exit为true时,while循环退出,exit的默认值为false。在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值,

2. 使用interrupt方法终止线程

使用interrupt方法来终端线程可分为两种情况:

(1)线程处于阻塞状态,如使用了sleep方法。

(2)使用while(!isInterrupted()){...}来判断线程是否被中断。

在第一种情况下使用interrupt方法,sleep方法将抛出一个InterruptedException例外,而在第二种情况下线程将直接退出。下面的代码演示了在第一种情况下使用interrupt方法。

[java] view plain copy print ?
  1. <SPAN style="FONT-FAMILY: SimSun">public class ThreadInterrupt extends Thread    
  2. {    
  3.     public void run()    
  4.     {    
  5.         try   
  6.         {    
  7.             sleep(50000);  // 延迟50秒     
  8.         }    
  9.         catch (InterruptedException e)    
  10.         {    
  11.             System.out.println(e.getMessage());    
  12.         }    
  13.     }    
  14.     public static void main(String[] args) throws Exception    
  15.     {    
  16.         Thread thread = new ThreadInterrupt();    
  17.         thread.start();    
  18.         System.out.println("在50秒之内按任意键中断线程!");    
  19.         System.in.read();    
  20.         thread.interrupt();    
  21.         thread.join();    
  22.         System.out.println("线程已经退出!");    
  23.     }    
  24. }  </SPAN>  
上面代码的运行结果如下:

在50秒之内按任意键中断线程!

sleep interrupted

线程已经退出!

在调用interrupt方法后, sleep方法抛出异常,然后输出错误信息:sleep interrupted。注意:在Thread类中有两个方法可以判断线程是否通过interrupt方法被终止。一个是静态的方法interrupted(),一个是非静态的方法isInterrupted(),这两个方法的区别是interrupted用来判断当前线是否被中断,而isInterrupted可以用来判断其他线程是否被中断。因此,while (!isInterrupted())也可以换成while (!Thread.interrupted())。

                                        java线程进阶_第1张图片



线程的状态(State)

  新生状态(New): 当一个线程的实例被创建即使用new关键字和Thread类或其子类创建一个线程对象后,此时该线程处于新生(new)状态,处于新生状态的线程有自己的内存空间,但该线程并没有运行,此时线程还不是活着的(not alive);

  就绪状态(Runnable): 通过调用线程实例的start()方法来启动线程使线程进入就绪状态(runnable);处于就绪状态的线程已经具备了运行条件,但还没有被分配到CPU即不一定会被立即执行,此时处于线程就绪队列,等待系统为其分配CPCU,等待状态并不是执行状态; 此时线程是活着的(alive);

  运行状态(Running): 一旦获取CPU(被JVM选中),线程就进入运行(running)状态,线程的run()方法才开始被执行;在运行状态的线程执行自己的run()方法中的操作,直到调用其他的方法而终止、或者等待某种资源而阻塞、或者完成任务而死亡;如果在给定的时间片内没有执行结束,就会被系统给换下来回到线程的等待状态;此时线程是活着的(alive);

  阻塞状态(Blocked):通过调用join()、sleep()、wait()或者资源被暂用使线程处于阻塞(blocked)状态;处于Blocking状态的线程仍然是活着的(alive)

  死亡状态(Dead):当一个线程的run()方法运行完毕或被中断或被异常退出,该线程到达死亡(dead)状态。此时可能仍然存在一个该Thread的实例对象,当该Thready已经不可能在被作为一个可被独立执行的线程对待了,线程的独立的call stack已经被dissolved。一旦某一线程进入Dead状态,他就再也不能进入一个独立线程的生命周期了。对于一个处于Dead状态的线程调用start()方法,会出现一个运行期(runtime exception)的异常;处于Dead状态的线程不是活着的(not alive)。

 线程状态图

       java线程进阶_第2张图片

Ø线程的方法(Method)、属性(Property)

1)优先级(priority)

每个类都有自己的优先级,一般property用1-10的整数表示,默认优先级是5,优先级最高是10;优先级高的线程并不一定比优先级低的线程执行的机会高,只是执行的机率高;默认一个线程的优先级和创建他的线程优先级相同;

2)Thread.sleep()/sleep(long millis)

当前线程睡眠/millis的时间(millis指定睡眠时间是其最小的不执行时间,因为sleep(millis)休眠到达后,无法保证会被JVM立即调度);sleep()是一个静态方法(static method) ,所以他不会停止其他的线程也处于休眠状态;线程sleep()时不会失去拥有的对象锁。 作用:保持对象锁,让出CPU,调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留一定的时间给其他线程执行的机会;

3)Thread.yield()

  让出CPU的使用权,给其他线程执行机会、让同等优先权的线程运行(但并不保证当前线程会被JVM再次调度、使该线程重新进入Running状态),如果没有同等优先权的线程,那么yield()方法将不会起作用。

4)thread.join()

 使用该方法的线程会在此之间执行完毕后再往下继续执行。

5)object.wait()

  当一个线程执行到wait()方法时,他就进入到一个和该对象相关的等待池(Waiting Pool)中,同时失去了对象的机锁—暂时的,wait后还要返还对象锁。当前线程必须拥有当前对象的锁,如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常,所以wait()必须在synchronized block中调用。

6)object.notify()/notifyAll()

  唤醒在当前对象等待池中等待的第一个线程/所有线程。notify()/notifyAll()也必须拥有相同对象锁,否则也会抛出IllegalMonitorStateException异常。

7)Synchronizing Block

 Synchronized Block/方法控制对类成员变量的访问;Java中的每一个对象都有唯一的一个内置的锁,每个Synchronized Block/方法只有持有调用该方法被锁定对象的锁才可以访问,否则所属线程阻塞;机锁具有独占性、一旦被一个Thread持有,其他的Thread就不能再拥有(不能访问其他同步方法),方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。
 
 
【借鉴于】:
http://blog.csdn.net/wangjinyu501/article/details/8787066

你可能感兴趣的:(java,多线程)