1. 程式人生 > >Java高並發-多線程基礎

Java高並發-多線程基礎

nts mark read extends 機會 應用 source 進程 void

一、什麽是線程

線程是進程內的執行單元。

二、線程的基本操作

2.1 狀態與操作

技術分享圖片

2.2 新建線程

Thread t1 = new Thread(new CreateThread());
t1.start();

# 直接覆蓋run方法
# 傳target實例,即Runnable接口實例

技術分享圖片

2.3 終止線程

技術分享圖片

2.4 中斷線程

public void Thread.interrupt(); // 中斷線程
public boolean Thread.isInterrupted(); // 判斷是否被中斷
public static boolean Thread.interrupted(); // 判斷是否被中斷,並清除當前中斷狀態
public static native void sleep(long millis) throws InterruptedException

代碼

// 線程t1
public void run() {
    while(true) {
        Thread.yield();
    }
}
// 對線程t1進行中斷操作,線程t1並不會做出響應
t1.interrupt();

// 執行下面這段代碼的線程,會對中斷做出響應
public void run() {
    while(true) {
        if (Thread.currentThread().isInterrupted
()) { System.out.println("Interrupted!"); break; } Thread.yield(); } }

sleep代碼

// 在等待的過程中,也對中斷操作做出響應
public void run() {
    while(true) {
        if (Thread.currentThread().isInterrupted()) {
            System.out.println('Interrupted!');
            break
; } try { Thread.sleep(2000); } catch (InterruptedException e) { System.out.println("Interrupted When Sleep"); // 設置中斷狀態,拋出異常後會清除中斷標記位 Thread.currentThread().interrupt(); } Thread.yield(); } }

2.5 掛起和繼續執行線程

suspend()不會釋放鎖

如果加鎖發生在resume()之前,則發生死鎖

這兩個就法不推薦使用

技術分享圖片

模擬死鎖:

public class BadSuspend {
    public static Object u = new Object();
    static ChangeObjectThread t1 = new ChangeObjectThread("t1");
    static ChangeObjectThread t2 = new ChangeObjectThread("t2");
    public static class ChangeObjectThread extends Thread {
        public ChangeObjectThread(String name) {
            super.setName(name);
        }
        @Override
        public void run() {
            synchronized(u) {
                System.out.println("in " + getName());
                Thread.currentThread().suspend();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        t1.start();
        Thread.sleep(100);
        t2.start();
        t1.resume();
        t2.resume();
        t1.join();
        t2.join();
    }
}

分析:

t1線程正常結束,t2線程死鎖

技術分享圖片

技術分享圖片

2.6 等待線程結束和謙讓

join,yeild

// 把自己占用的CPU機會釋放掉,再和別人一起競爭CPU
public static native void yield();

// 當前線程未做完,主線程等待當前線程做完後再往下走
public final void join() throws InterruptedException
public final synchronized void join(long millis) throws InterruptedException

技術分享圖片

技術分享圖片

三、守護線程

在後臺默默地完成一些系統性的服務,比如垃圾回收線程、JIT線程就可以理解為守護線程

當一個Java應用內,只有守護線程時,Java虛擬機就會自然退出

技術分享圖片

四、線程優先級

高優先級的線程更容易在競爭中獲勝

t1.setPriority(Thread.MAX_PRIORITY);

五、基本的線程同步操作

5.1 synchronized

指定加鎖對象:對給定對象加鎖,進入同步代碼前要獲得給定對象的鎖。

直接作用於實例方法:相當於對當前實例加鎖,進入同步代碼前要獲得當前實例的鎖。

直接作用於靜態方法:相當於對當前類加鎖,進入同步代碼前要獲得當前類的鎖。

指定加鎖對象

public class AccountingSync implements Runnable {
    static AccountingSync instance = new AccountingSync();
    static int i = 0;
    @Override
    public void run() {
        for (int j=0;j<100000;j++) {
            synchronized(instance) {
                i++;
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start(); 
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
}

技術分享圖片

作用於實例方法:註意多個線程要對同一個實例加鎖

public class AccountingSync2 implements Runnable {
    static AccountingSync2 instance = new AccountingSync2();
    static int i = 0;
    public synchronized void increase() {
        i++;
    }
    @Override
    public void run() {
        for (int j=0;j<100000;j++) {
            increase();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start(); 
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
}

技術分享圖片

作用於靜態方法:註意區別 作用於實例方法

技術分享圖片

5.2 wait()/notify()

notify()之後,允許線程往下走,但是如果沒有獲得鎖的話,也還是執行不了,t2.notifyAll()之後t1就可以往下執行了,但是此時t1還未獲得object鎖,必須等t2睡2秒後,獲得object鎖後執行。

技術分享圖片

技術分享圖片

Java高並發-多線程基礎