天天看點

Java的多線程1:線程的使用0(9)#0(8)#0(7)#0(6)#0(5)#0(4)#0(3)#0(2)#0(1)#0(stop!)1(9)#1(8)#1(7)#1(6)#1(5)#1(4)#1(3)#1(2)#1(1)#1(stop!)2(9)#2(8)#2(7)#2(6)#2(5)#2(4)#2(3)#2(2)#2(1)#2(stop!)

Java的多線程1:線程的使用

概述

程序是線程的容器,線程共享程序的記憶體空間,是以線程之間彼此通信是比較容易的,而線程又有自己私有的記憶體位址,其他線程無法通路。了解程序和線程關系,可以看我另一篇部落格《程序與線程》

Java建立線程的兩種方式

繼承Thread類

public class ThreadDemo1 extends Thread {

@Override
public void run(){
    for (int i = 0; i < 10; i++) {
        System.out.println("目前執行的線程是" + Thread.currentThread().getName());
    }
}

public static void main(String[] args) {
    ThreadDemo1 threadDemo1 = new ThreadDemo1();
    ThreadDemo1 threadDemo2 = new ThreadDemo1();
    threadDemo1.start();
    threadDemo2.start();
}           

}

執行結果是不确定的

實作Runnable

public class ThreadDemo2 implements Runnable {

@Override
public void run() {
    for (int i = 0; i < 10; i++) {
        for (int j = 0;j < 1000; ++j){
            System.out.println(i + "目前執行的線程是" + Thread.currentThread().getName());
        }
    }
}

public static void main(String[] args) {
    ThreadDemo2 threadDemo1 = new ThreadDemo2();
    ThreadDemo2 threadDemo2 = new ThreadDemo2();
    Thread thread1 = new Thread(threadDemo1);
    Thread thread2 = new Thread(threadDemo2);
    thread1.start();
    thread2.start();
    System.out.println("目前線程是===>" + Thread.currentThread().getName());
}           

主線程的名字為main,非主線程的名字是由虛拟機來指定的,同時,我們也可以為線程指定具體的名稱。

我們保證每個線程都能正常啟動,并不意味着它會按順序的執行,因為排程程式是無法保證它的執行次序的,同時,run()函數結束時,意味着該線程的任務完成了。

注意:調用線程要調用start,如果調用run,那僅僅是簡單的對象調用。

線程生命周期

public enum State {
    /**
     * Thread state for a thread which has not yet started.
     */
    NEW,

    /**
     * Thread state for a runnable thread.  A thread in the runnable
     * state is executing in the Java virtual machine but it may
     * be waiting for other resources from the operating system
     * such as processor.
     */
    RUNNABLE,

    /**
     * Thread state for a thread blocked waiting for a monitor lock.
     * A thread in the blocked state is waiting for a monitor lock
     * to enter a synchronized block/method or
     * reenter a synchronized block/method after calling
     * {@link Object#wait() Object.wait}.
     */
    BLOCKED,

    /**
     * Thread state for a waiting thread.
     * A thread is in the waiting state due to calling one of the
     * following methods:
     * <ul>
     *   <li>{@link Object#wait() Object.wait} with no timeout</li>
     *   <li>{@link #join() Thread.join} with no timeout</li>
     *   <li>{@link LockSupport#park() LockSupport.park}</li>
     * </ul>
     *
     * <p>A thread in the waiting state is waiting for another thread to
     * perform a particular action.
     *
     * For example, a thread that has called <tt>Object.wait()</tt>
     * on an object is waiting for another thread to call
     * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
     * that object. A thread that has called <tt>Thread.join()</tt>
     * is waiting for a specified thread to terminate.
     */
    WAITING,

    /**
     * Thread state for a waiting thread with a specified waiting time.
     * A thread is in the timed waiting state due to calling one of
     * the following methods with a specified positive waiting time:
     * <ul>
     *   <li>{@link #sleep Thread.sleep}</li>
     *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
     *   <li>{@link #join(long) Thread.join} with timeout</li>
     *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
     *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
     * </ul>
     */
    TIMED_WAITING,

    /**
     * Thread state for a terminated thread.
     * The thread has completed execution.
     */
    TERMINATED;
}
           

建立狀态

線程對象建立後,就進入建立狀态  Thread thread = new Thread

就緒狀态

調用start()方法,線程進入就緒狀态,但并不意味着線程就立即執行,隻是說明此線程已經做好準備,随時等待CPU排程執行。

阻塞狀态

多個線程同時競争一個獨占鎖,其他未搶到鎖的線程,就進入阻塞狀态被放置到鎖池中,直到擷取到鎖,進入就緒狀态

等待狀态

該線程需要等待其他線程做出一些特定動作,通知或者是中斷,等待其被其他線程喚醒,像CountDownLatch就可以等待一個或者幾個線程結束。

逾時等待狀态

和等待狀态不同的是,它可以在指定的時間自行的傳回,sheep(long)函數就會讓線程進入逾時等待狀态,時間到了才會轉入就緒狀态。

運作狀态(Running)

CPU排程處于就緒狀态的線程時,這個線程才真正執行,進入運作狀态。

終止狀态

線程正常執行完畢後或提前強制性終止或出現異常,線程就要銷毀,釋放資源。

線程的方法調用

擷取線程基本資訊

public class ThreadDemo6 {

public static void main(String[] args) {
    Thread thread = new Thread(){
        @Override
        public void run(){
            /*擷取線程唯一id辨別*/
            long id = this.getId();
            System.out.println("thread的ID==>" + id);

            /*擷取線程名字*/
            String name = this.getName();
            System.out.println("thread的名字==>" + name);

            /*擷取線程的優先級 預設5 1-10*/
            int priority = this.getPriority();
            System.out.println("thread的優先等級==>" + priority);

            /*檢視目前線程是否為守護線程*/
            boolean isDaemon = this.isDaemon();
            System.out.println("thread是否為守護線程==>" + isDaemon);

            /*檢視線程是否被中斷*/
            boolean isInterrupted = this.isInterrupted();
            System.out.println("thread是否被中斷==>" + isInterrupted);
        }
    };
    thread.start();
}           

執行結果

thread的ID==>11

thread的名字==>Thread-0

thread的優先等級==>5

thread是否為守護線程==>false

thread是否被中斷==>false

Thread.yield()

public class ThreadDemo1 implements Runnable {

protected int countDown = 10;
private static int taskCount = 0;
private final int id = taskCount++;
public ThreadDemo1(){}
public ThreadDemo1(int countDown){
    this.countDown = countDown;
}
public String status(){
    return "#" + id + "(" + (countDown > 0 ? countDown : "stop!") + ")";
}
           
@Override
public void run() {
    while (countDown-- > 0){
        System.out.println(status() + "  ");
        Thread.yield();
    }
}

public static void main(String[] args) {
    for (int i = 0; i < 3; i++){
        new Thread(new ThreadDemo1()).start();
    }
}           

0(9)#0(8)#0(7)#0(6)#0(5)#0(4)#0(3)#0(2)#0(1)#0(stop!)

1(9)#1(8)#1(7)#1(6)#1(5)#1(4)#1(3)#1(2)#1(1)#1(stop!)

2(9)#2(8)#2(7)#2(6)#2(5)#2(4)#2(3)#2(2)#2(1)#2(stop!)

這個是一個倒計時的任務,對Thread.yield()調用是對線程排程器的一種建議,它在聲明“我已經執行完生命周期中最重要的部分了,此刻正是切換給其他任務執行一段時間的大好時機”,說白就是自己按暫停鍵,讓出自己CPU的使用權限,轉為就緒狀态,至于下一次什麼時候能獲得CPU排程就不一定了,有時很快,有時得等上一會。

Thread.sleep

protected int countDown = 10;
private static int taskCount = 0;
private final int id = taskCount++;
public ThreadDemo1(){}
public ThreadDemo1(int countDown){
    this.countDown = countDown;
}
public String status(){
    return "#" + id + "(" + (countDown > 0 ? countDown : "stop!") + ")";
}
           
@Override
public void run() {
    try {
        while (countDown-- > 0){
            System.out.println(status());
            Thread.sleep(1000);
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}

public static void main(String[] args) {
    for (int i = 0; i < 3; i++){
        new Thread(new ThreadDemo1()).start();
    }
}           

Thread.sleep(long)将使“正在執行的任務“中止執行給定的時間(暫停執行)并且讓出CPU使用權,這個語句相當于說在接下來的1秒内,你都不要叫我,我想睡一會,1秒睡眠時間過後,它自動轉為就緒狀态,但CPU不一定馬上執行這個睡醒的線程,這要取決于是否搶到CPU時間片段。值得注意的是如果sleep和yield上下文被加鎖了,它們依然使用鎖,不會去釋放。而sleep與yield最大的不同是,yield不會讓線程進入等待狀态,隻是把線程轉為就緒狀态,并把CPU執行機會讓步給優先級相同或者更高的線程,而sleep能控制具體交出CPU的使用時間。

Thread.currentThread()

public class ThreadDemo2 extends Thread {

static {
    System.out.println("靜态塊執行的線程===>" + Thread.currentThread().getName());
}
{
    System.out.println("非靜态塊執行的線程是====>" + Thread.currentThread().getName());
    System.out.println("this.getName()1=====>" + this.getName());
}

public ThreadDemo2(){
    System.out.println("構造方法内執行的線程====>" + Thread.currentThread().getName());
    System.out.println("this.getName()2=====>" + this.getName());
}

@Override
public void run() {
    System.out.println("目前執行的線程為====>" + Thread.currentThread().getName());
    System.out.println("this.getName()3=====>" + this.getName());
}

public static void main(String[] args) {
    ThreadDemo2 threadDemo2 = new ThreadDemo2();
    threadDemo2.start();
}           

靜态塊執行的線程===>main

非靜态塊執行的線程是====>main

this.getName()1=====>Thread-0

構造方法内執行的線程====>main

this.getName()2=====>Thread-0

目前執行的線程為====>Thread-0

this.getName()3=====>Thread-0

currentThread傳回的是目前正在執行線程對象的引用,它與this.getName()有明顯的不同,執行靜态塊,非靜态塊,構造方法的線程是main,而非ThreadDemo2,在執行run()方法的才是執行個體化的線程threadDemo2。是以在目前執行的Thread未必就是Thread本身。

isAlive()

public class ThreadDemo3 extends Thread {

@Override
public void run(){
    System.out.println("執行執行====" + this.isAlive());
}

public static void main(String[] args) {
    ThreadDemo3 threadDemo3 = new ThreadDemo3();
    System.out.println("begin===>" + threadDemo3.isAlive());
    threadDemo3.start();
    System.out.println("end==>" + threadDemo3.isAlive());
}           

begin===>false

end==>true

執行執行====true

isAlive()檢測線程是否處于活動狀态,活動狀态傳回true

setPriority()

優先級設定,優先級高的線程越容易擷取CPU使用權,

public class ThreadDemo4 {

public static void main(String[] args) {
    for (int i = 0; i < 5; ++i){
        Thread1 thread1 = new Thread1();
        thread1.setPriority(6);
        Thread2 thread2 = new Thread2();
        thread2.setPriority(4);
        thread2.start();
        thread1.start();
    }
}           

class Thread1 extends Thread{

@Override
public void run(){
    for (int i = 0; i < 100000; ++i){
        System.out.println("+++++++++++++");
    }
}           

class Thread2 extends Thread{

@Override
public void run(){
    for (int i = 0; i < 100000; ++i){
        System.out.println("--------------");
    }
}           

+++++++++++++

...

CPU會将資源盡量讓給優先級高的線程

setDaemon()

守護線程,也有人叫後天線程,我們建立出來的線程預設都是前台線程,在使用上來說,守護線程和前台線程是沒啥差別,差別在于程序結束,當一個程序中的所有前台線程都結束時,無論這個程序中的守護線程是否還在運作都要強制将他們結束。也就是說前台線程都結束了,守護線程也會自動銷毀,它是為其他線程提供便利而存在的。

/rose與jack/

public class ThreadDemo5 {

public static void main(String[] args) {
    Rose rose = new Rose();
    Jack jack = new Jack();
    /*設定為守護線程必須線上程未啟動之前*/
    jack.setDaemon(true);
    rose.start();
    jack.start();
}           

class Rose extends Thread{

@Override
public void run(){
    for (int i = 0; i < 5; ++i){
        System.out.println("rose: let me go!");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    System.out.println("成功跳水");
}           

class Jack extends Thread{

@Override
public void run(){
    while (true){
        System.out.println("jack:you jump! i jump!");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}           

rose: let me go!

jack:you jump! i jump!

成功跳水

jack守護着rose,jack是守護線程,當rose跳水後,jack認為自己也沒有活着的必要了,也自己銷毀了,但注意一點是這當中還有一個第三者main,需要main也運作完jack線程才會銷毀。

join()

這個方法可以協調多個線程同步運作,多線程運作本身是設計異步運作的,但在程式運作業務中,有可能線程A的計算需要線程B的傳回結果,這就需要他們執行各自任務時要有先後,join就需要協調這些線程同步運作。

private static boolean isFinish = false;

public static void main(String[] args) {
    Thread download = new Thread(){
        @Override
        public void run(){
            System.out.println("下載下傳圖檔中.....");
            for (int i = 1; i <= 100; ++i){
                System.out.println("下載下傳進度" + i + "%");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("圖檔下載下傳完畢");
            isFinish = true;
        }
    };
    Thread show = new Thread(){
        @Override
        public void run(){
            System.out.println("開始顯示圖檔...");
            try {
                download.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (!isFinish){
                throw new RuntimeException("圖檔下載下傳出錯");
            }
            System.out.println("圖檔正常展示。。。");
        }
    };
    download.start();
    show.start();
}           

下載下傳圖檔中.....

開始顯示圖檔...

下載下傳進度1%

下載下傳進度2%

下載下傳進度100%

圖檔下載下傳完畢

圖檔正常展示。。。

show調用join會使show無限阻塞,直到down線程銷毀為止,它和sleep最大的差別是join會釋放鎖,而sleep不會。

涉及到jmm記憶體模型,線程安全等,後面在介紹

原文位址

https://www.cnblogs.com/dslx/p/12664004.html