java學習day11——線程

併發和並行

併發:指兩個或多個事件在同一個時間段內發生(交替執行
並行:指兩個或多個事件在同一時刻發生(同時發生,多個CPU)
在這裏插入圖片描述

線程和進程

進程:指一個內存中運行的應用程序,每個進程都有一個獨立的內存空間,一個應用程序可以同時運行多個進程;進程也是程序的一次執行過程,是系統運行程序的基本單位;系統運行一個程序即是一個進程從創建、運行到消亡的過程。

在這裏插入圖片描述
線程:線程是進程中的一個執行單元,負責當前進程中程序的執行,一個進程中至少有一個線程。一個進程中是可以有多個線程的,這個應用程序也可以稱之爲多線程程序
在這裏插入圖片描述

線程的調度

分時調度

所有線程輪流使用CPU的使用權,平均分配每個線程佔用CPU的時間

搶佔式調度

優先讓優先級高的線程使用CPU,如果線程優先級相同,那麼會隨機選擇一個(線程隨機性),Java使用的爲搶佔式調度
可以在任務管理器中設置優先級

主線程

主線程:執行主(main)方法的線程

單線程程序:java程序中只有一個線程
執行從main方法開始,從上到下依次執行
在這裏插入圖片描述

創建線程類

Java使用java.lang.Thread類代表線程,所有的線程對象都必須是Thread類或其子類的實例。

創建多線程程序的第一種方式:創建Thread類的子類

實現步驟:

  1. 創建一個Thread類的子類
  2. 在Thread類的子類種重寫Thread類種的run方法,設置線程任務(開啓線程要做什麼)
  3. 創建Thread類的子對象
  4. 調用Thread類中的start方法,開啓新的線程,執行run方法

public void start()使該線程開始執行;Java 虛擬機調用該線程的 run 方法。
結果是兩個線程併發地運行;當前線程(main線程)和另一個線程(創建的新線程,執行其 run 方法)。
多次啓動一個線程是非法的。特別是當線程已經結束執行後,不能再重新啓動。
java程序屬於搶佔式調度,哪個線程的優先級高,哪個線程優先執行;同一個優先級,隨機選擇一個執行

// 1. 創建一個Thread類的子類
public class MyThread extends Thread {
    // 2. 在Thread類的子類中重寫Thread類中的run方法,設置線程任務
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("run:"+i);
        }
    }
}
public class Demo01Thread {
    public static void main(String[] args) {
        // 3. 創建Thread類的子對象
        MyThread mt = new MyThread();
        // 4. 調用Thread類中的start方法,開啓新的線程,執行run方法
        mt.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("main:"+i);
        }
    }
}

出現隨機性打印結果
在這裏插入圖片描述
在這裏插入圖片描述

多線程內存圖解

在這裏插入圖片描述
當調用mt.start()方法,就開闢新的佔空間,程序變成多線程,由cpu去做選擇

Thread類的常用方法

獲取線程的名稱

  1. 使用Thread類中的方法getName().

String getName()

  1. 可以先獲取到當前正在執行的線程,使用線程中的方法getName()獲取線程的名稱

public static Thread currentThread()返回對當前正在執行的線程對象的引用。

public class MyThread extends Thread{
    // 重寫run()方法,設置線程任務

    @Override
    public void run() {
        // 1. 使用Thread類中的方法getName().
//        String name = getName();
//        System.out.println(name); // 結果形式  Thread-1

        // 2. 可以先獲取到當前正在執行的線程,使用線程中的方法getName()獲取線程的名稱
        Thread t = Thread.currentThread();
        System.out.println(t);  // 結果形式  Thread[Thread-1,5,main]

        // 鏈式編程
        System.out.println(Thread.currentThread().getName()); // 結果形式  Thread-1
    }
}
public class Demo01GetThreadName {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
        new MyThread().start();
        new MyThread().start();

        // 鏈式編程
        System.out.println(Thread.currentThread().getName());//main
    }
}

設置線程的名稱

  1. 使用Thread類中的方法setName(名字)

public final void setName(String name)改變線程名稱,使之與參數 name 相同。

  1. 創建一個帶參數的構造方法,參數傳遞線程的名稱,調用父類的帶參構造方法,把線程名稱傳遞給父類,讓父類(Thread)給子線程起一個名字
    Thread(String name) 分配新的 Thread 對象。
public class MyThread extends Thread{
    // 重寫run()方法,設置線程任務
    public MyThread(){}
    public MyThread(String name){
        super(name);//把線程名稱傳遞給父類,讓父類(Thread)給子線程起一個名字
    }
    @Override
    public void run() {
       // 獲取線程名稱
        System.out.println(Thread.currentThread().getName());
    }
}
public class Demo01GetThreadName {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.setName("小強");
        mt.start();

        new MyThread("旺財").start();
    }
}

sleep

public static void sleep(long millis) :使當前正在執行的線程以指定的毫秒數暫停(暫時停止執行)。毫秒數結束之後,線程繼續執行。
throws InterruptedException在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行),此操作受到系統計時器和調度程序精度和準確性的影響。該線程不丟失任何監視器的所屬權。

public class Demo02Sleep {
    public static void main(String[] args) {
        for (int i = 0; i < 60; i++) {
            System.out.println(i);
            try {
                Thread.sleep(1000);//單位是毫秒
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

創建多線程程序的第二種方式:聲明實現 Runnable 接口的類

Runnable 接口應該由那些打算通過某一線程執行其實例的類來實現。類必須定義一個稱爲 run 的無參數方法。
java.lang.Thread類的構造方法
Thread(Runnable target) :分配新的 Thread 對象。
Thread(Runnable target, String name) :分配新的 Thread 對象。

實現步驟:

  1. 創建一個Runnable接口的實現類
  2. 在實現類中重寫Runnable接口的run方法,設置線程任務
  3. 創建一個Runnable接口的實現類對象
  4. 創建Thread類對象,構造方法中傳遞Runnable接口的實現類對象
  5. 調用Thread類中的start方法,開啓新的線程執行run方法
//1. 創建一個Runnable接口的實現類
public class RunnableImpl implements Runnable {
    // 2. 在實現類中重寫Runnable接口的run方法,設置線程任務
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}
public class Demo01Runnable {
    public static void main(String[] args) {
        //3. 創建一個Runnable接口的實現類對象
        RunnableImpl run = new RunnableImpl();
        //4. 創建Thread類對象,構造方法中傳遞Runnable接口的實現類對象
        Thread t = new Thread(run);
        // 5. 調用Thread類中的start方法,開啓新的線程執行run方法
        t.start();
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}

Thread和Runnable的區別

  1. Runnable避免了單繼承的侷限性
    一個類只能繼承一個類,繼承了Thread類就不能繼承其他類
    實現了Runnable接口,還可以繼承其他的類,實現其他的接口
  2. Runnable增強了程序的擴展性,降低了程序的耦合性(解耦)
    實現Runnable接口的方式,把設置線程任務和開啓新線程進行了分離(解耦)
    實現類中,重寫了run方法:用來設置線程任務
    創建Thread對象,調用start方法,用來開啓新線程

匿名內部類

匿名內部類方式實現線程的創建
匿名:沒有名字
內部類:寫在其他類內部的類
匿名內部類作用:簡化代碼
把子類繼承父類,重寫父類的方法,創建子類對象合一步完成
把實現類實現接口,重寫接口中的方法,創建實現類對象合成一步完成
匿名內部類的最終產物是:子類/實現類對象,而這個類沒有名字
格式:
new 父類/接口(){
重寫父類/接口中的方法
};

public class Demo01InnerClassThread {
    public static void main(String[] args) {
        new Thread(){
            // 重寫run方法

            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName()+"-->"+"AAA");
                }
            }
        }.start();
        // 線程的接口Runnable
        Runnable r = new Runnable(){
            //重寫run方法
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName()+"-->"+"BBB");
                }
            }
        };
        new Thread(r).start();
        //簡化接口的方式
        new Thread(new Runnable(){
            //重寫run方法
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName()+"-->"+"CCC");
                }
            }
        }).start();
    }
}

注意事項

  1. 匿名內部類,在【創建對象】的時候,只能使用唯一一次。
  2. 匿名對象,在【調用方法】的時候,只能調用唯一一次。
    如果希望同一個對象,調用多次方法,那麼必須給對象起個名字
  3. 匿名內部類是省略了【實現類/子類名稱】,但是匿名對象是省略了【對象名稱】
    強調:匿名內部類和匿名對象不是一回事!!!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章