Java多線程(二)認識Java裏面的Thread

轉載請註明出處:http://blog.csdn.net/github_39430101/article/details/77340996

創建線程的三種方法

java.lang.Thread類是線程類,其每一個實例表示一個可以併發運行的線程。我們可以通過繼承該類並重寫run方法來定義一個具體的線程。其中重寫run方法的目的的定義該線程要執行的邏輯。啓動線程時調用線程的start()方法而非直接調用run()方法。start()方法會將當前線程納入線程調度,使當前線程可以開始併發運行。當線程獲取時間片段後會自動開始執行run方法中的邏輯。

繼承Thread類

public class TestThread extends Thread{
    @Override
    public void run(){
        for(int i=0; i<100; i++){
            System.out.println("我是線程");
        }
    }
}

創建和啓動線程:

public class ThreadApp{
    public static void main(String[] args){
        Thread thread = new TestThread(); //實例化線程
        thread.start();  //啓動線程
    }
}

實現Runnable創建並啓動線程

實現Runnable接口並重寫run()方法來定義線程體,然後再創建線程的時候講Runnable的的實例傳入並啓動線程。這樣做的好處在於可以將線程與線程要執行的任務分離開減少耦合,同時Java是單繼承的,定義一個類實現Runnable接口這樣的做法可以更好的去實現其他父類或接口。因爲接口是多繼承關係。這裏我們講一下靜態代理設計模式。

靜態代理

  • 真實角色
  • 代理角色 :持有真實角色的引用
  • 二者實現相同的接口
public class StaticProxy {
    public static void main(String[] args) {
        //創建真實角色
        You you = new You();
        //創建代理角色+真實角色的引用
        Agency agency = new Agency(you);
        agency.renting();
    }
}
//創建一個租房的接口,裏面有租房方法
interface Rent{
    //公共方法租房
    void renting();
}
//真實角色
class You implements Rent{
    @Override
    //租房
    public void renting(){
        System.out.println("我租房子");
    }
}
//代理角色,中介公司
class Agency implements Rent{
    Rent you;
    public Agency(){

    }
    public Agency(Rent you){
        this.you = you;
    }
    //租房前要做的
    private void before(){
        System.out.println("找中介公司");
    }
    private void after(){
        System.out.println("交房租");
    }
    @Override
    public void renting(){
        before();
        you.renting();
        after();
    }
}
//輸出:
找中介公司
我租房子
交房租

Runnable相當於我們的租房接口,Thread類也是實現它的,我們只需要實現它並重寫run()方法,其他的事情交給中介Thread類

//真實角色類
public class TestRunnable implements Runnable{
    @Override
    public void run(){
        for(int i=0; i<100; i++){
            System.out.println("我是線程");
        }
    }
}

啓動線程

public class ThreadApp{
    public static void main(String[] args){
        Runnable runnable = new TestRunnable();
        Thread thread = new Thread(runnable); //實例化線程並傳入線程體
        thread.start(); //啓動線程
    }
}

實現Callable接口

Callable是類似於Runnable的接口,實現Callable接口的類和實現Runnable的類都是可被其他線程執行的任務。Callable和Runnable有幾點不同:

  • Callable規定的方法是call(),而Runnable規定的方法是run()
  • call()方法可拋出異常,而run()方法是不能拋出異常的
  • Callable的任務執行後可返回值,運行Callable任務可拿到一個Future對象,而Runnable的任務是不能返回值的。Future標識異步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,並檢索計算的結果。通過Future對象可瞭解任務執行情況,可取消任務的執行,還可獲取任務執行的結果。
public class Call {
    public static void main(String[] args) {
        Callable<Integer> callable = new Callable<Integer>() {
            public Integer call() throws Exception {
                return new Random().nextInt(100);
            }
        };
        FutureTask<Integer> future = new FutureTask<Integer>(callable);
        new Thread(future).start();
        try {
            Thread.sleep(5000);
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

線程API

Thread的靜態方法currentThread方法可以用於獲取運行當前代碼片段的線程。

Thread current = Thread.currentThread();

獲取線程信息

long getId()    //返回該線程的標識符
String getName()   //返回該線程的名稱
int getPriority()  //返回線程的優先級
Thread.state getState()  //獲取線程的狀態
boolean isAlive() //測試線程是否處於活動狀態
boolean isDaemon() //測試線程是否爲守護線程
boolean isInterrupted() //測試線程是否已經中斷

常用方法:

void run()  //創建該類的子類時必須實現的方法
void start()  //開啓線程的方法
static void sleep(long t) //釋放CPU的執行權,不釋放鎖。調用的目的是不讓當前線程霸佔該進程所獲取的CPU資源,以留一定的時間給其他線程執行的機會
final void wait() //釋放CPU的執行權,釋放鎖。當一個線程執行到wait()方法是,它就進入到一個和該對象相等的等待池(Waiting Pool)中,同時失去了對象的鎖-暫時的,wait後還要返還對象鎖。當前線程必須擁有對前對象的鎖,如果當前線程不是此鎖的擁有者,會拋出IllegalMonitorStateException異常,所以wait()必須在synchronized block中調用
final void notify()/notifyAll() //喚醒在當前對象等待池中等待的第一個線程/所有線程。notify/notifyAll()也必須擁有相同對象鎖,否則也會拋出IllegalMonitorStateException異常
static void yield() //該方法用於使當前線程主動讓出當次CPU時間片回到Runnable狀態,等待分配時間片。
void join()  //該方法用於等待當前線程結束,是一個阻塞方法。

線程的狀態

這裏寫圖片描述

  • 新建狀態(new):當線程對象創建後,即進入了新建狀態,如:Thread t = new MyThread();
  • 就緒狀態(Runnable):當調用線程對象的start()方法線程即進入就緒狀態。處於就緒狀態的線程,只是說明此線程已經做好了等待CPU調度分配時間片的準備,並不是說執行了start()方法此線程就立即會執行。
  • 運行狀態(Running):當CPU開始調度處於就緒狀態的線程時,此線程纔開始真正執行,即進入到運行狀態。
  • 阻塞狀態(Blocked):處於運行狀態中的線程由於某種原因,暫時放棄對CPU的使用權,停止執行,此時進入阻塞狀態,直到其進入就緒狀態纔有機會再次被CPU調用。阻塞狀態可分爲三種:

    1.等待阻塞:運行狀態中的線程執行wait()方法,使本線程進入到等待阻塞狀態
    2.同步阻塞:線程在獲取synchronized同步鎖(鎖被其他線程所佔用),它會進入同步阻塞狀態
    3.其他阻塞:通過調用線程的sleep()或join()或發出了IO請求時,線程會進入到阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、後者IO處理完畢時,線程重新轉入就緒狀態。

  • 死亡狀態(Dead):線程執行完了或者因爲異常退出了run()方法,該線程結束生命週期

停止線程

  • 自然終止:線程體正常執行完畢
  • 外部干涉
    1.線程類中定義線程體使用的標識
    2.線程體使用該標識
    3.提供對外的方法改變該標識
    4.外部根據條件調用該方法
public class Demo {
    public static void main(String[] args) {
        Study s = new Study();
        new Thread(s).start();
        for(int i=0; i<100; i++){
            if(i == 50){
                s.stop();
            }
            System.out.println("main...."+i);
        }
    }
}
class Study implements Runnable{
    private boolean flag = true;
    @Override
    public void run(){
        while(flag){
            System.out.println("study thread...");
        }
    }
    public void stop(){
        this.flag = false;
    }
}

如果線程是阻塞的,則不能使用上面的方法來終止線程

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("線程已經退出!");
  }
}

線程阻塞

join

public class Demo extends Thread{
    public static void main(String[] args) {
        Demo demo = new Demo();
        Thread t = new Thread(demo);
        t.start();
        for(int i=0;i<1000;i++){
            if(i == 50){
                try {
                    t.join();  //main阻塞
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("main............."+i);
        }
    }
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println("join....."+i);
        }
    }
}

這裏寫圖片描述
當main執行到49的時候開始阻塞了,執行join。

yield

public class Demo extends Thread {
    public static void main(String[] args) {
        Demo demo = new Demo();
        Thread t = new Thread(demo);
        t.start();
        for(int i=0;i<1000;i++){
            if(i % 20 ==0){
                Thread.yield(); //暫停本線程
            }
            System.out.println("main............."+i);
        }
    }
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println("join....."+i);
        }
    }
}

sleep

休眠,不釋放鎖。常用於

  • 與時間相關:倒計時
  • 模擬網絡延時

倒計時

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

/*
 * 倒計時
 * 倒數10個數,一秒內打印一個
 */

public class Demo extends Thread {
    public static void main(String[] args) throws InterruptedException {
        Date endTime = new Date(System.currentTimeMillis()+10*1000);
        System.out.println(endTime);
        long end = endTime.getTime();
        while(true){
            //輸出
            System.out.println(new SimpleDateFormat("mm:ss").format(endTime));
            //等待一秒
            Thread.sleep(1000);
            //構建下一秒的時間
            endTime = new Date(endTime.getTime()-1000);
            //10秒內繼續,否則退出
            if((end-10000)>endTime.getTime()){
                break;
            }
        }

    }
}
發佈了35 篇原創文章 · 獲贊 30 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章