在作業系統中,我們透過分時的方法在CPU上不斷地切換處理多個進程任務,給人並行處理的感覺,這種方法在作業系統中叫做多任務。多任務在較低層次上擴展出多執行緒的概念,也就是指一個程式同時執行多個執行緒。這種可以同時運行一個以上的執行緒的程序,我們叫做多執行緒程序。
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方法中的語句。這時便創建並啟動了一個線程。
說到這裡我們再來看看API中的start方法:
public void start( ) 使該執行緒開始執行;
Java 虛擬機器呼叫該執行緒的 run 方法。
結果是兩個執行緒並發地運行;目前執行緒(從呼叫傳回 start 方法)和另一個執行緒(執行其 run 方法)。
多次啟動一個執行緒是非法的。特別是當執行緒已經結束執行後,不能再重新啟動。
public Thread(ThreadGroup group,Runnable target, String name,long stackSize)其他的建構子都是這四個參數的組合,那麼我們來看看這四個參數的含義:
这是一种具有平台依赖性的参数,stackSize能指定堆栈的大小。 在某些平台上,指定一个较高的 stackSize 参数值可能使线程在抛出 StackOverflowError 之前达到较大的递归深度。stackSize 参数的值与最大递归深度和并发程度之间的关系细节与平台有关。在某些平台上,stackSize 参数的值无论如何不会起任何作用。 作为建议,可以让虚拟机自由处理 stackSize 参数。
Thread中还有很多很多实用的方法,我们在涉及到具体概念的时候再给予介绍。
我们既然创建了线程,那么这个线程在什么时候终止呢?有两种情况:
当线程的run方法执行方法体中最后一条语句后,正常退出而自然死亡。
出现了在方法中没有捕获的异常,此时终止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六种,我们来逐个分析一下这几种状态的含义:
这里的创建新的线程真的是仅仅new了一个线程。
new Thread(r);
创建新的线程,是指刚刚new出来的线程,这个线程没有通过start的方法来启动。
那么一旦我们调用了start方法,这个线程开始工作。这是他就处于可运行状态,这个可运行状态不只是包含线程运行的时候,线程在中断的时候也被算为可运行状态。一个可运行状态的线程可能在运行也可能没在运行,我们不要因为一个线程在可运行的状态下没运行而急躁,很有可能这个线程的终止只是为了让其他的线程获得机会。
当一个线程试图去获得一个内部锁时,但这个内部锁被其他的线程持有,这个时候,为了等待去使用这个内部锁,这个线程将会暂时处在被阻塞的状态。当其他线程释放锁的时候,这个线程获得了内部锁,并且从阻塞状态转变为非阻塞状态。
当一个线程等待另一个线程通知调度器一个条件时,这个线程自己进入等待状态。等待状态和阻塞状态很类似,但是他们是存在本质区别的。如果另一个线程通知调度器结束,那么这个线程进行工作,等待状态也随之结束。
计时等待和等待是比较相似的,计时等待是表示他有一个超时参数。调用他们导致线程会进入计时等待。这个状态将一直保持到超市期满或者接收到适当的通知。相比较直接的等待,变得更加的安全。
线程的终止,我们在上面的线程中断中有所提及。线程终止理论上只有两种情况:当线程的run方法执行方法体中最后一条语句后,正常退出而自然死亡。2. 出现了在方法中没有捕获的异常,此时终止run方法,意外死亡。
线程中有很多的属性,尽管我在API中只看到一些其中一小部分的字段,但是线程的这种理念,在方法中也有所体现。线程的属性有以下这些:线程优先级,守护线程,处理未捕获异常的处理器。
在java中,每个线程都有一个优先级,我们需要知道的是,在没经过特殊处理的时候,所有的线程优先级都是一样的。默认的,我们把优先级分成1到10之间,高优先级的线程会先被操作。说到这里不由得让我们想起了操作系统中的进程优先级,和一些类似老化这样的名词。实际上,java虚拟机也确实是用进程的优先级来类比出线程的优先级,这样做最大的一个问题就在于,每个操作系统对于进程优先级的处理并不相同,java的线程优先级也因此而具有平台变化。实际上,我们不应该把程序的正确性依赖于线程优先级,我们应该尽量少的使用线程优先级。我们在这里介绍了线程优先级,但是我们并不建议去使用它。
守护线程是指通过:
t.setDaemon(ture); //将线程转换为守护线程
这样的写法,将一个线程转换为守护线程。守护线程的作用是为其他的线程提供服务,如果其他所有的线程都被退出,只剩下守护线程,那么程序也就结束了。没有去单独运行守护线程的必要。比如说其他线程的计时器,我们就可以将它设置为一个守护线程。
run方法不能抛出任何被检查,不被检测的异常会导致线程终止。在这种情况下,线程就死亡了。在这种情况下,我们需要实现一个Thread.UncaughtExceptionHandler。我们可以通过这种方式来知道,真正让我们的run意外死亡的问题在哪里。
在java的并发程序中,我们先了解了这些线程的状态和属性,下面我们就可以来研究关于线程之间同步的问题。如何能几个线程同时运行,让他们之间配合的好。
在操作系统中,我们通过分时的方法在CPU上不断地切换处理多个进程任务,给人并行处理的感觉,这种方法在操作系统中叫做多任务。多任务在较低层次上扩展出多线程的概念,也就是指一个程序同时执行多个线程。这种可以同时运行一个以上的线程的程序,我们叫做多线程程序。
以上就是java线程(一)—线程状态及属性详解的内容,更多相关内容请关注PHP中文网(www.php.cn)!