首頁 > Java > java教程 > java線程(一)—線程狀態及屬性詳解

java線程(一)—線程狀態及屬性詳解

黄舟
發布: 2017-03-01 11:38:32
原創
1617 人瀏覽過


在作業系統中,我們透過分時的方法在CPU上不斷地切換處理多個進程任務,給人並行處理的感覺,這種方法在作業系統中叫做多任務。多任務在較低層次上擴展出多執行緒的概念,也就是指一個程式同時執行多個執行緒。這種可以同時運行一個以上的執行緒的程序,我們叫做多執行緒程序。

(一)建立執行緒

1.建立執行緒實例

##百聞不如一見,與其先說線程的各種狀態,不如我們先直接示範如何建立一個新的執行緒。

package Thread;/**
 * 
 * @author QuinnNorris
 * 创建线程实例
 */public class NewThread {

    /**
     * @param args
     */
    public static void main(String[] args) {        // TODO Auto-generated method stub

        Runnable r = new Run();        //创建一个类对象
        Thread th = new Thread(r);        //由Runnable创建一个Thread对象
        th.start();        //调用start方法,运行新的线程,即运行的是th中的run方法
    }

}
登入後複製

我們再看看Run這個實作了Runnable介面的類別的情況:

package Thread;/**
 * 
 * @author QuinnNorris
 * Run类实现了Runnable接口
 */public class Run implements Runnable {

    @Override
    public void run() {        // TODO Auto-generated method stub
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
登入後複製

建立並運行一個執行緒大概是這樣的,我們先建立一個類別去實作Runnable接口,在其中實作Runnable介面中唯一的run方法,這方法就是執行緒啟動後自動呼叫的方法。之後我們在主類別中建立一個Run類別的對象,將這個物件作為建構器的參數傳入Thread物件中,之後我們用Thread物件的參數呼叫start方法,start方法會自動的呼叫run方法,開始執行run方法中的語句。這時便創建並啟動了一個線程。

2.start方法和run方法

要注意的是,我們不能直接呼叫run方法因為這樣不會建立一個新的線程,而是呼叫到一個已經存在的線程中間,start會為你建立新線程,並自動呼叫run。我們在任何情況下都不需要自己手動呼叫run方法。

說到這裡我們再來看看API中的start方法:

public void start( )  使該執行緒開始執行;
 Java 虛擬機器呼叫該執行緒的 run 方法。  
 結果是兩個執行緒並發地運行;目前執行緒(從呼叫傳回 start 方法)和另一個執行緒(執行其 run 方法)。  
 多次啟動一個執行緒是非法的。特別是當執行緒已經結束執行後,不能再重新啟動。

3.Runnable類別

我們已經知道如何按部就班的創建一個線程,但這並不能滿足我們的好奇心,我們要直接將這兩個類別一探究竟,看看其中到底有什麼方法和屬性。 Runnable類別中沒有屬性,只有一個run方法。這個介面應該由那些打算透過某一執行緒執行其實例的類別來實現。而Thread類別卻比較的複雜。

(二)Thread類別

1.Thread類別實作了Runnable介面

所以上例中Run類別有一個理所當然的寫法就是繼承Thread類,因為Thread實作了Runnable,這兩種寫法其實都是重寫run方法。但面試題非常喜歡問你:Thread和Runnable這兩種實現多執行緒的方式哪一個好?這時應該可以回答Runnable方法好的。原因也很簡單,繼承只能有一個,介面可以有很多,Runnable是介面不阻礙這個類別繼承的彈性。

2.Thread建構器

Thread的建構子有很多,參數主要有這四種:

public Thread(ThreadGroup group,Runnable target, String name,long stackSize)
登入後複製

其他的建構子都是這四個參數的組合,那麼我們來看看這四個參數的含義:

1.ThreadGroup group

ThreadGroup是線程組的意思,它可以對一批線程進行分類管理。對線程組的控管理,也就是同時控制線程組裡面的這一行線程。使用者建立的所有執行緒都屬於指定執行緒組,如果沒有顯示指定屬於哪個執行緒組,那麼該執行緒就屬於預設執行緒組(即main執行緒組)。預設情況下,子執行緒和父執行緒處於同一個執行緒組。只有在創建線程時才能指定其所在的線程組,線程運行中途不能改變它所屬的線程組,也就是說線程一旦指定所在的線程組,就直到該線程結束。線程組與線程之間結構類似樹形的結構,線程只能訪問自己線程組的信息,不能訪問父線程組或其他線程組內的信息。

2.Runnable target

target就是其run方法被呼叫的物件。如果這個參數是null,則這個Thread類別中要自己重寫run方法,否則這個Thread類別的run方法就是呼叫這個Runnable類別中的run方法。

3.String name

每個執行緒都有一個自己的名字,其實這個參數是很少被使用的,有的時候我們命名一個執行緒並不用這種形式。但是如果我們不去填這個參數的話,線程會自動的新產生一個名稱。自動產生的名稱的形式為 “Thread-“+n ,其中的 n 為整數。

4.long stackSize

这是一种具有平台依赖性的参数,stackSize能指定堆栈的大小。 在某些平台上,指定一个较高的 stackSize 参数值可能使线程在抛出 StackOverflowError 之前达到较大的递归深度。stackSize 参数的值与最大递归深度和并发程度之间的关系细节与平台有关。在某些平台上,stackSize 参数的值无论如何不会起任何作用。 作为建议,可以让虚拟机自由处理 stackSize 参数。

Thread中还有很多很多实用的方法,我们在涉及到具体概念的时候再给予介绍。

(三)中断线程

我们既然创建了线程,那么这个线程在什么时候终止呢?有两种情况:

  1. 当线程的run方法执行方法体中最后一条语句后,正常退出而自然死亡。

  2. 出现了在方法中没有捕获的异常,此时终止run方法,意外死亡。

从这两点中,我们可以看出,其实我们并没有特殊的手段可以人为的去在中间干涉线程的中断(Thread中的stop方法或许有这种作用,但是这个方法已经被废弃,我们不要使用它)。虽然没有强制结束线程的方法,但是我们可以用interrupt方法请求终止线程。要注意“请求”这个词,没有任何语言方面的表述是要求一个被中断的线程应该被终止。我们去用interrupt方法中断一个线程不过是引起他的注意。被中断的线程可以决定如何去响应这个中断的请求。

当对一个线程调用interrupt方法时,线程的中断状态将被置位。这个中断状态是每个线程都有的boolean标志。每个线程都会不时地检查这个布尔值,判断这个线程是否被中断。
我们可以使用:

Thread.currentThread().isIntertrupted()
登入後複製

isInterrupt方法和interrupt很像,它是用来判断线程是否被中断。
我们可以通过interrupt方法来中断一个线程,但是值得注意的是,如果这个线程处于wait和sleep或者在join方法调用过程中,中断线程将会报一个InterruptedException异常,我们可以通过try-catch块来捕获这种异常。

(四)线程状态

线程一共有6种状态,这六种状态不是我们规定的,而是在Thread中的内部嵌套类State中规定的。这六种状态分别是New,Runnable,Blocked,Waiting,Timed waiting,Terminated六种,我们来逐个分析一下这几种状态的含义:

1.New 新创建的线程

这里的创建新的线程真的是仅仅new了一个线程。

new Thread(r);
登入後複製

创建新的线程,是指刚刚new出来的线程,这个线程没有通过start的方法来启动。

2.Runnable 可运行

那么一旦我们调用了start方法,这个线程开始工作。这是他就处于可运行状态,这个可运行状态不只是包含线程运行的时候,线程在中断的时候也被算为可运行状态。一个可运行状态的线程可能在运行也可能没在运行,我们不要因为一个线程在可运行的状态下没运行而急躁,很有可能这个线程的终止只是为了让其他的线程获得机会。

3.Blocked 被阻塞

当一个线程试图去获得一个内部锁时,但这个内部锁被其他的线程持有,这个时候,为了等待去使用这个内部锁,这个线程将会暂时处在被阻塞的状态。当其他线程释放锁的时候,这个线程获得了内部锁,并且从阻塞状态转变为非阻塞状态。

4.Wait 等待

当一个线程等待另一个线程通知调度器一个条件时,这个线程自己进入等待状态。等待状态和阻塞状态很类似,但是他们是存在本质区别的。如果另一个线程通知调度器结束,那么这个线程进行工作,等待状态也随之结束。

5.Timed waiting 计时等待

计时等待和等待是比较相似的,计时等待是表示他有一个超时参数。调用他们导致线程会进入计时等待。这个状态将一直保持到超市期满或者接收到适当的通知。相比较直接的等待,变得更加的安全。

6.Terminated 终止

线程的终止,我们在上面的线程中断中有所提及。线程终止理论上只有两种情况:当线程的run方法执行方法体中最后一条语句后,正常退出而自然死亡。2. 出现了在方法中没有捕获的异常,此时终止run方法,意外死亡。

(五)线程属性

线程中有很多的属性,尽管我在API中只看到一些其中一小部分的字段,但是线程的这种理念,在方法中也有所体现。线程的属性有以下这些:线程优先级,守护线程,处理未捕获异常的处理器。

1.线程优先级

在java中,每个线程都有一个优先级,我们需要知道的是,在没经过特殊处理的时候,所有的线程优先级都是一样的。默认的,我们把优先级分成1到10之间,高优先级的线程会先被操作。说到这里不由得让我们想起了操作系统中的进程优先级,和一些类似老化这样的名词。实际上,java虚拟机也确实是用进程的优先级来类比出线程的优先级,这样做最大的一个问题就在于,每个操作系统对于进程优先级的处理并不相同,java的线程优先级也因此而具有平台变化。实际上,我们不应该把程序的正确性依赖于线程优先级,我们应该尽量少的使用线程优先级。我们在这里介绍了线程优先级,但是我们并不建议去使用它。

2.守护线程

守护线程是指通过:

t.setDaemon(ture); //将线程转换为守护线程
登入後複製

这样的写法,将一个线程转换为守护线程。守护线程的作用是为其他的线程提供服务,如果其他所有的线程都被退出,只剩下守护线程,那么程序也就结束了。没有去单独运行守护线程的必要。比如说其他线程的计时器,我们就可以将它设置为一个守护线程。

3.处理未捕获异常的处理器

run方法不能抛出任何被检查,不被检测的异常会导致线程终止。在这种情况下,线程就死亡了。在这种情况下,我们需要实现一个Thread.UncaughtExceptionHandler。我们可以通过这种方式来知道,真正让我们的run意外死亡的问题在哪里。

(六)总结

在java的并发程序中,我们先了解了这些线程的状态和属性,下面我们就可以来研究关于线程之间同步的问题。如何能几个线程同时运行,让他们之间配合的好。

在操作系统中,我们通过分时的方法在CPU上不断地切换处理多个进程任务,给人并行处理的感觉,这种方法在操作系统中叫做多任务。多任务在较低层次上扩展出多线程的概念,也就是指一个程序同时执行多个线程。这种可以同时运行一个以上的线程的程序,我们叫做多线程程序。

 以上就是java线程(一)—线程状态及属性详解的内容,更多相关内容请关注PHP中文网(www.php.cn)!


來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板