1. 程式人生 > >Java 多執行緒(1)

Java 多執行緒(1)

Java 多執行緒(1)

1. 概述

瞭解作業系統後,想學 Java多執行緒 好久了。一開始我學習了《Think In Java》中的多執行緒,真心講的很好。但是,有的過於深刻。需要,自己慢慢體會、總結。所以,我又入手了《Java 多執行緒程式設計核心技術》。打算,回頭再看《Think In Java》。

我計劃以《Java 多執行緒程式設計核心技術》為線,寫一下自己對 Java多執行緒 的理解。

2. Java執行緒基礎

【1】執行緒安全

引起執行緒安全問題的主要因素是 不同執行緒對共享資源的訪問 。執行緒安全是學習 多執行緒技術 的一個關鍵問題。

這裡需要明白 什麼是對資源的共享訪問、什麼是不共享。看下面的程式碼:

package Multithreading;

public class MyThread extends Thread{
    private int count = 2;
    public MyThread(String name){
        super();
        this.setName(name);
    }

    @Override
    public void run() {
        super.run();
        while (count > 0){
            System.out.println(Thread.currentThread().getName() + "\tCount = " + count);
            count--;
        }
    }
}

package Multithreading;

public class Run {
    public static void main(String[] args) {
        MyThread myThread0 = new MyThread("執行緒-0");
        MyThread myThread1 = new MyThread("執行緒-1");
        MyThread myThread2 = new MyThread("執行緒-2");
        myThread0.start();
        myThread1.start();
        myThread2.start();
    }
}

很明顯,這裡為三個執行緒 訪問的是三個不同 的物件。所以,它們之間沒有影響,它們坑定執行緒安全。執行結果如下:

這裡寫圖片描述

將上面的 Run類 改為如下:

package Multithreading;

public class Run {
    public static void main(String[] args) {
        MyThread task = new MyThread("執行緒-0");
        new Thread(task).start();
        new Thread(task).start();
        new Thread(task).start();
    }
}

得到如下結果:

這裡寫圖片描述

可以看到這裡的 task物件 不再是 資源與執行緒兩種身份,這裡它只作為一種資源。所以,下面建立的三個匿名的執行緒是對同一個資源進行訪問的。所以,結果會發生改變,因為 Count <= 0 後其他的執行緒也發現並退出了。
共享同一個資源會造成 非執行緒安全。比如,一個執行緒正要讀某共享的資源,而現在排程到一個新的執行緒執行,這個新執行緒正好修改那個共享資源。以後,重新排程到原執行緒讀該共享資源時會發現它不是原來所需要的,這就是所謂的 “讀寫衝突”。如何解決這樣的問題呢,可以通過 加鎖 機制。。。

【2】this.getName() 的問題

在《Java 多執行緒程式設計核心技術》p17,對 this.getName() 方法有點疑問。於是,寫程式碼測試了一下:

package Multithreading;

public class CountOperate extends Thread {

    @Override
    public synchronized void run() {
        super.run();
        System.out.println("CountOperate---begin---CountOperate");
        System.out.println("this.getName()\t" + this.getName());
        System.out.println("this.isAlive()\t" + this.isAlive());
        System.out.println("Thread\t" + Thread.currentThread().getName());
        System.out.println("Thread\t" + Thread.currentThread().isAlive());
        System.out.println("CountOperate----end----CountOperate");
    }
}

package Multithreading;

public class Run {
    public static void main(String[] args) {
        CountOperate countOperate = new CountOperate();
        countOperate.start();
        Thread thread1 = new Thread(countOperate);
        thread1.start();
    }
}

執行結果如下:

這裡寫圖片描述

分析結果可以看到,Thread類 對執行緒的命名方式,它是從 Thread-0 開始的。比如這裡的第一個 Thread物件countOperate ,它的 getName() 方法返回就是 Thread-0。而,第二個 Thread物件thread1,它的 getName() 方法返回就是 Thread-1。知道了這些後,就可以弄懂書上的結果了。

【3】interrupted()isInterrupted()

為了區別它們,先看看它們的宣告

public static boolean interrupted() {
    return currentThread().isInterrupted(true); //true 表示清理中斷標誌
}

public boolean isInterrupted() {
    return isInterrupted(false);    //false 表示不清理中斷標誌
}

通過分析它們的宣告,可以很清楚的認識它們。interrupted() 是靜態方法,它可以用來判斷當前執行執行緒的中斷標誌,可以在呼叫 currentThread() 看出來。而且,它還每次都清楚中斷標誌。對於 isInterrupted() 方法,它是用來判斷某個 執行緒物件 的中斷標誌,且它並不清理中斷標誌。

【4】異常法停止中斷

停止中斷的常用方法!

package Multithreading;

public class MyThread extends Thread{

    @Override
    public void run() {
        super.run();
        try {
            for (int i = 0; i < 500000; i++) {
                System.out.println("i = " + i);
                if (this.isInterrupted()) {
                    System.out.println("已經進入停止狀態!我要退出!");
                    throw new InterruptedException();
                }
            }
        }catch (Exception e){
            System.out.println("進入退出異常!");
            e.printStackTrace();
        }
    }
}
//ps:注意這裡 try-catch 的位置
package Multithreading;

public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
        Thread.sleep(1000);
        myThread.interrupt();
    }
}

【5】suspend 獨佔

在《Java 多執行緒程式設計核心技術》p38,書中說的不是很清楚(或者我的理解有誤)。這裡,仍然可以通過在最後使用 thread1.resume() 使得可以繼續執行。但是,不建議使用 suspend() 和 resume() 方法。

ps:在《Java 多執行緒程式設計核心技術》p40,是有一 很大機率 不列印 main end。但是還是有機率輸出的。為了測試,可以在 i++; 後面新增 Thread.yield()

【6】執行緒優先順序的繼承特性

對於 繼承 的理解。這裡的繼承是說,新的執行緒在舊的執行緒上開啟而不同於類的繼承。

也就是說,在舊執行緒裡啟動的新執行緒擁有舊執行緒的優先順序。

3. 堅持。。。

第一章的結束,用了一上午效率有點低。

一天進步一點點。。。