频道栏目
首页 > 资讯 > Java > 正文

黑马程序员_java线程进阶

13-04-12        来源:[db:作者]  
收藏   我要投稿

一、线程的生命周期
      这里所说的线程的生命周期,也是根据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]
<SPAN style="FONT-FAMILY: SimSun">public class LifeCycle extends Thread   
{   
    public void run()   
    {   
        int n = 0;   
        while ((++n) < 1000);           
    }   
        
    public static void main(String[] args) throws Exception   
    {   
        LifeCycle thread1 = new LifeCycle();   
        System.out.println("isAlive: " + thread1.isAlive());   
        thread1.start();   
        System.out.println("isAlive: " + thread1.isAlive());   
        thread1.join();  // 等线程thread1结束后再继续执行     
        System.out.println("thread1已经结束!");   
        System.out.println("isAlive: " + thread1.isAlive());   
    }   
}  </SPAN> 

public class LifeCycle extends Thread 

    public void run() 
    { 
        int n = 0; 
        while ((++n) < 1000);         
    } 
      
    public static void main(String[] args) throws Exception 
    { 
        LifeCycle thread1 = new LifeCycle(); 
        System.out.println("isAlive: " + thread1.isAlive()); 
        thread1.start(); 
        System.out.println("isAlive: " + thread1.isAlive()); 
        thread1.join();  // 等线程thread1结束后再继续执行  
        System.out.println("thread1已经结束!"); 
        System.out.println("isAlive: " + thread1.isAlive()); 
    } 
}  要注意一下,在上面的代码中使用了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]
<SPAN style="FONT-FAMILY: SimSun">class TestThreadMethod extends Thread{   
public static int shareVar = 0;   
public TestThreadMethod(String name){   
super(name);   
}   
public void run(){   
for(int i=0; i<4; i++){   
System.out.print(Thread.currentThread().getName());   
System.out.println(" : " + i);   
//Thread.yield(); (1)    
/* (2) */   
try{   
Thread.sleep(3000);   
}   
catch(InterruptedException e){   
System.out.println("Interrupted");   
}}}   
}   
public class TestThread{   
public static void main(String[] args){   
TestThreadMethod t1 = new TestThreadMethod("t1");   
TestThreadMethod t2 = new TestThreadMethod("t2");   
t1.setPriority(Thread.MAX_PRIORITY);   
t2.setPriority(Thread.MIN_PRIORITY);   
t1.start();   
t2.start();   
}   
} </SPAN> 

class TestThreadMethod extends Thread{ 
public static int shareVar = 0; 
public TestThreadMethod(String name){ 
super(name); 

public void run(){ 
for(int i=0; i<4; i++){ 
System.out.print(Thread.currentThread().getName()); 
System.out.println(" : " + i); 
//Thread.yield(); (1) 
/* (2) */ 
try{ 
Thread.sleep(3000); 

catch(InterruptedException e){ 
System.out.println("Interrupted"); 
}}} 

public class TestThread{ 
public static void main(String[] args){ 
TestThreadMethod t1 = new TestThreadMethod("t1"); 
TestThreadMethod t2 = new TestThreadMethod("t2"); 
t1.setPriority(Thread.MAX_PRIORITY); 
t2.setPriority(Thread.MIN_PRIORITY); 
t1.start(); 
t2.start(); 

}
运行结果为:


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


t1 : 0  t1 : 1  t1 : 2  t1 : 3  t2 : 0  t2 : 1  t2 : 2  t2 : 3
可见,调用yield(),不同优先级的线程永远不会得到执行机会。

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

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

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

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

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

[java]
<SPAN style="FONT-FAMILY: SimSun">public class ThreadFlag extends Thread   
{   
    public volatile boolean exit = false;   
  
    public void run()   
    {   
        while (!exit);   
    }   
    public static void main(String[] args) throws Exception   
    {   
        ThreadFlag thread = new ThreadFlag();   
        thread.start();   
        sleep(5000); // 主线程延迟5秒    
        thread.exit = true;  // 终止线程thread    
        thread.join();   
        System.out.println("线程退出!");   
    }   
}  </SPAN> 

public class ThreadFlag extends Thread 

    public volatile boolean exit = false; 
 
    public void run() 
    { 
        while (!exit); 
    } 
    public static void main(String[] args) throws Exception 
    { 
        ThreadFlag thread = new ThreadFlag(); 
        thread.start(); 
        sleep(5000); // 主线程延迟5秒 
        thread.exit = true;  // 终止线程thread 
        thread.join(); 
        System.out.println("线程退出!"); 
    } 
}  在上面代码中定义了一个退出标志exit,当exit为true时,while循环退出,exit的默认值为false。在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值,

2. 使用interrupt方法终止线程

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

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

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

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

[java]
<SPAN style="FONT-FAMILY: SimSun">public class ThreadInterrupt extends Thread   
{   
    public void run()   
    {   
        try  
        {   
            sleep(50000);  // 延迟50秒    
        }   
        catch (InterruptedException e)   
        {   
            System.out.println(e.getMessage());   
        }   
    }   
    public static void main(String[] args) throws Exception   
    {   
        Thread thread = new ThreadInterrupt();   
        thread.start();   
        System.out.println("在50秒之内按任意键中断线程!");   
        System.in.read();   
        thread.interrupt();   
        thread.join();   
        System.out.println("线程已经退出!");   
    }   
}  </SPAN> 

public class ThreadInterrupt extends Thread 

    public void run() 
    { 
        try
        { 
            sleep(50000);  // 延迟50秒 
        } 
        catch (InterruptedException e) 
        { 
            System.out.println(e.getMessage()); 
        } 
    } 
    public static void main(String[] args) throws Exception 
    { 
        Thread thread = new ThreadInterrupt(); 
        thread.start(); 
        System.out.println("在50秒之内按任意键中断线程!"); 
        System.in.read(); 
        thread.interrupt(); 
        thread.join(); 
        System.out.println("线程已经退出!"); 
    } 
}  上面代码的运行结果如下:

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

sleep interrupted

线程已经退出!

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

                                       

 


线程的状态(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)。

 线程状态图

      

Ø线程的方法(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就不能再拥有(不能访问其他同步方法),方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。


 

相关TAG标签
上一篇:PHP交叉编译和移植
下一篇:Android layer-list(边框加粗效果)
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站