Java線程學習筆記之線程簡介

什麼是線程

幾乎每一種操作系統都支持進程的概念——進程就是在某種程度上相互隔離的、獨立運行的程序。
相互獨立的進程
線程化是允許多個活動共存於一個進程中的工具。大多數現代的操作系統都支持線程,而且線程的概念以各種形式已經存在了好多年。Java是第一個在語言本身中顯式地包含線程的主流編程語言,他沒有把線程化看做是底層操作系統的工具。
有時候,線程也稱作輕量級進程。就像進程一樣,線程在程序中是獨立的、併發的執行路徑,每個線程都有它自己的堆棧、自己的程序技術器和自己的局部變量。但是,與分隔的進程相比,進程中的線程之間的隔離程度要小。它們共享內存、文件句柄和其他每個進程應有的狀態。
進程可以支持多個線程,它們看似同時執行,但互相之間並不同步。
每個Java程序都至少有一個線程——主線程。當一個Java程序啓動時,JVM會創建主線程,並在該線程中調用程序的main()方法。

創建線程

在Java程序中創建線程有幾種方法。每個Java程序至少包含一個線程:主線程。其他線程都是通過Thread構造器或實例化繼承類Thread的類來創建。
例如通過實例化繼承類創建一個線程:

public class Test extends Thread{

    public static final int Max_r = 1000000;
    public static final int Ten_s = 10000;

    public volatile boolean finished = false;
    //必須要實現run方法,因爲線程要直接調用run方法,執行run方法裏面的邏輯
    public void run(){
        int[] prims = new int[Max_r];
        int count = 0;

        for(int i=2; count<Max_r;i++){
            if(finished)
                break;

            boolean prime = true;
            for(int j = 0; j<count;j++){
                if(i%prims[j]==0){
                    prime = false;
                    break;
                }
            }

            if(prime){
                prims[count++] = i;
                System.out.println("Found prime: "+i);
            }
        }
    }

    public static void main(String[] args){
        Test test = new Test();//實例化繼承了Thread類的Test類,新建一個線程
        test.start();//執行線程
        try{
            Thread.sleep(5000);//使線程休眠5秒
        }
        catch(InterruptedException e){

        }

        test.finished = true;
    }
}

在一個線程對新線程的Thread對象調用start()方法之前,這個新線程並沒有真正開始執行。Thread對象在其線程真正啓動之前就已經存在了,而且其線程退出之後仍然存在。

結束線程

線程會以以下三種方式之一結束:

  • 線程到達其run()方法的末尾
  • 線程拋出一個未捕獲的Exception或Error
  • 另一個線程調用一個棄用的方法——stop()方法。

加入線程

Thread API包含了一個等待另一個線程完成的方法:join()方法。當調用Thread join()方法時,調用線程將阻塞,直到目標線程完成爲止。

還有,永遠不要假設一個線程會在另一個線程之前執行某些操作,除非您已經使用了同步以強制一個特定的執行順序。因爲不同的機器執行的順序不同,即使是同一臺機器,相同的代碼執行順序也不同。可以通過這個例子來看:

public class TenThread {
    private static class WorkerThread extends Thread {
        int rdata;

        public WorkerThread(int rdata) {
            this.rdata = rdata;
        }

        public void run(){
            rdata = rdata + 100;
            System.out.println(Thread.currentThread().getName()+":"+rdata);
        }

        public int getRdata() {
            return rdata;
        }

        public void setRdata(int rdata) {
            this.rdata = rdata;
        }

    }

    public static void main(String[] args){
        WorkerThread[] threads = new WorkerThread[10];
        int max = Integer.MIN_VALUE;

        for(int i=0;i<10;i++){
            threads[i] = new WorkerThread((int)(Math.random()*100));
            threads[i].start();
        }

        try{
            for(int i=0;i<10;i++){
                threads[i].join();
                System.out.println(threads[i].getName()+"————"+threads[i].getRdata());
                max = Math.max(max, threads[i].getRdata());
            }
        }catch(InterruptedException e){

        }
        System.out.println("Maximum value was " + max);
    }
}

執行結果:

Thread-1:153
Thread-9:114
Thread-8:143
Thread-0:118
Thread-6:116
Thread-7:177
Thread-5:196
Thread-4:171
Thread-2:168
Thread-3:114
Thread-0————118
Thread-1————153
Thread-2————168
Thread-3————114
Thread-4————171
Thread-5————196
Thread-6————116
Thread-7————177
Thread-8————143
Thread-9————114
Maximum value was 196

再執行一次:

Thread-0:139
Thread-2:143
Thread-4:163
Thread-1:164
Thread-3:124
Thread-5:197
Thread-6:114
Thread-7:196
Thread-8:114
Thread-0————139
Thread-1————164
Thread-9:170
Thread-2————143
Thread-3————124
Thread-4————163
Thread-5————197
Thread-6————114
Thread-7————196
Thread-8————114
Thread-9————170
Maximum value was 197

休眠

Thread API包含了一個sleep()方法,它將使當前線程進入等待狀態,知道過了一段時間,或者知道另一個線程對當前線程的Thread對象調用了Thread.interrupt()方法,從而中斷了線程。當過了指定時間後,線程又將變成可運行的,並且回到調度程序的可運行線程隊列中。
如果線程是由對Thread.interrupt()的調用而中斷,那麼休眠的線程會拋出InterruotedException,這樣線程就知道它是由中斷喚醒的,就不必查看計時器是否過期。例如通過下面的代碼喚醒線程並捕獲InterruotedException異常:

import java.util.Date;


public class TwoThread {

    public static class Thread1 extends Thread {
        public void run() {
            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName()+"live");//捕獲異常
            }
        }
    }

    public static class Thread2 extends Thread {
        public void run() {
            for(int i=0;i>=0;i++){
                System.out.println(new Date());
            }
        }
    }

    public static void main(String[] args) {
        Thread2 t2 = new Thread2();
        t2.start();
        Thread1 t1 = new Thread1();
        t1.start();
        try{
            Thread.sleep(2000);
        }catch (InterruptedException e) {
            // 這裏捕獲的異常就不執行其他代碼了
        }
        t1.interrupt();
        t2.stop();
    }
}

Thread.yield()方法就像sleep()一樣,但它並不引起休眠,而是暫停當前線程片刻,這樣其他線程就可以運行了。在大多數實現中,當較高優先級的線程調用Thread.yield()時,較低優先級的線程就不會運行。

守護程序線程

們提到過當 Java 程序的所有線程都完成時,該程序就退出,但這並不完全正確。隱藏的系統線程,如垃圾收集線程和由 JVM 創建的其它線程會怎麼樣?我們沒有辦法停止這些線程。如果那些線程正在運行,那麼 Java 程序怎麼退出呢?

這些系統線程稱作守護程序線程。Java 程序實際上是在它的所有非守護程序線程完成後退出的。

任何線程都可以變成守護程序線程。可以通過調用 Thread.setDaemon() 方法來指明某個線程是守護程序線程。您也許想要使用守護程序線程作爲在程序中創建的後臺線程,如計時器線程或其它延遲的事件線程,只有當其它非守護程序線程正在運行時,這些線程纔有用。

誰會創建線程

線程通過幾種機制進入java程序。除了用Thread構造器顯式創建線程之外,還可以用許多其他的機制來創建線程:

  • AWT和Swing
  • RMI
  • java.util.TimerTask工具
  • servlet和JSP技術

從jdk1.3開始。TimerTask工具被引入到Java語言。這個便利的工具可以讓你在稍後的某個時間執行任務或者定期執行任務。
實現Timer類非常簡單:創建一個計時器線程,並且構建一個安執行時間排序的等待時間隊列。TimerTask線程被標記成守護程序線程,這樣他就不會阻止程序退出。
Timer是守護線程。
因爲計時器時間實在計時器線程中執行,所以必須確保正確同步了針對計時器任務中使用的任何數據項的訪問。Timer類是線程安全的。

package threadtest;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerTaskTest extends Thread{

    @Override
    public void run() {
        // TODO Auto-generated method stub
        Timer timer = new Timer("timerThread"); 
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                System.out.println(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date())+"---"+Thread.currentThread().getName());
            }
        };
        timer.schedule(timerTask, 10000, 20000);//設置創建任務10秒後開始執行,之後每隔20秒執行一次任務
    }

    public static void main(String[] args){
        TimerTaskTest test = new TimerTaskTest();
        test.setDaemon(true);
        test.start();
        try {
            Thread.sleep(120000);//讓主線程休眠一段時間,從而讓新建的線程有時間執行,當主線程關閉的時候,其他線程也會關閉
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("主線程關閉:"+Thread.currentThread().getName());
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章