溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Java線程的基礎(chǔ)用法教程

發(fā)布時間:2021-10-14 15:25:37 來源:億速云 閱讀:111 作者:iii 欄目:編程語言

這篇文章主要講解了“Java線程的基礎(chǔ)用法教程”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Java線程的基礎(chǔ)用法教程”吧!

線程

線程和進(jìn)程

進(jìn)程是操作系統(tǒng)分配資源的最小單位,而線程是程序執(zhí)行的最小單位,他們都是可以并發(fā)執(zhí)行的。一個進(jìn)程至少有一個線程,這些線程共享進(jìn)程的資源空間。

線程簡介

每個線程都有一個優(yōu)先級,高優(yōu)先級的線程比低優(yōu)先級的線程先執(zhí)行。優(yōu)先級的取值范圍是1到10的整數(shù),默認(rèn)是5。每個線程有可能被標(biāo)記為一個守護(hù)線程。當(dāng)一個線程創(chuàng)建另外一個新的線程對象,新的線程的優(yōu)先級等于創(chuàng)建他的線程的優(yōu)先級;如果新的線程對象是一個守護(hù)線程當(dāng)且僅當(dāng)創(chuàng)建他的線程是一個守護(hù)線程。

線程分類

Java線程分為守護(hù)線程(Daemon Thread)用戶線程(User Thread)。守護(hù)線程和用戶線程基本上是一樣的,唯一的區(qū)別是如果用戶線程全部退出運(yùn)行了,不管有沒有守護(hù)線程虛擬機(jī)都會退出。守護(hù)線程的作用是為其他的線程的運(yùn)行提供服務(wù),最典型的守護(hù)線程就是GC(垃圾回收期)。

創(chuàng)建線程

創(chuàng)建線程的方式

創(chuàng)建一個線程類有三種方式:

  • 繼承Thread類

  • 實(shí)現(xiàn)Runnable接口

  • 實(shí)現(xiàn)Callable接口

Thread

Thread簡介

Thread是創(chuàng)建線程最關(guān)鍵的一個類,這個詞本身也代表線程,Thread類實(shí)現(xiàn)了Runnable接口。

代碼示例

public class ThreadDemo extends Thread {
	public void run() {
		for (int i = 0; i < 60; i++) {
			System.out.println(getName() + ":" + i);
		}
	}
}
public class Demo{
    public static void main(String[] args) {
        ThreadDemo t1 = new ThreadDemo();
		ThreadDemo t2 = new ThreadDemo();
		t1.start();
		t2.start();
    }
}

Runnable

Runnable簡介

Runnable是提供線程的接口,有一個抽象方法public abstract void run()。實(shí)現(xiàn)了這個接口的類必須實(shí)現(xiàn)它的run方法。

代碼示例

public class Runnable implements Runnable{
    public void run() {
       public void run() {
           for (int i = 0; i < 60; i++) {
				System.out.println(Thread.currentThread().getName() + ":" + i);
           }
	   }
	}
}
public class Demo{
    public static void main(String[] args) {
        RunnableDemo run = new RunnableDemo();
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        t1.start();
        t2.start();
    }
}

Callable和Future

Callable和Future簡介

Thread和Runnable創(chuàng)建線程不能獲取線程的返回值。從Java1.5開始,就提供了Callable和Future,通過他們可以在任務(wù)執(zhí)行完畢之后得到任務(wù)執(zhí)行結(jié)果。

  • Callable接口:可以返回一個結(jié)果或者拋出一個異常的一個任務(wù),實(shí)現(xiàn)者定義一個沒有參數(shù)的call方法。區(qū)別于Thread和Runnable的run方法,Calllable任務(wù)執(zhí)行的方法是call。

  • Future接口:Future接口代表了異步計(jì)算的結(jié)果,提供了一些方法用于檢查計(jì)算結(jié)果是否完成,獲取計(jì)算結(jié)果等。FutureTask類提供了Future接口的實(shí)現(xiàn),并且實(shí)現(xiàn)了Runnable接口。

代碼案例

public class MyCallable implements Callable<Integer> {
    public Integer call() {
        int sum = 0;
        for (int i = 0; i <= 100; i++) {
            sum += i;
        }
        return new Integer(sum);
    }
}
public class Demo{
    public static void main(String[] args) {
        MyCallable callable = new MyCallable();
        FutureTask<Integer> result = new FutureTask<Integer>(callable);
        new Thread(result).start();
        try {
            Integer value = result.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

線程生命周期

線程狀態(tài)

在Thread類中有一個內(nèi)部枚舉類State代表了線程的狀態(tài),一個線程從創(chuàng)建到銷毀就是一個完整的生命周期。

public enum State {
    /**
     * 線程被創(chuàng)建,還沒有開始運(yùn)行
     */
    NEW,
    /**
     * 線程運(yùn)行狀態(tài),運(yùn)行狀態(tài)的線程是正在被Java虛擬機(jī)執(zhí)行,但是也可能正在等待操作系統(tǒng)的其他資源例如處理器
     */
    RUNNABLE,
    /**
     * 線程阻塞狀態(tài),等待監(jiān)視器鎖。處于阻塞狀態(tài)線程是在等待監(jiān)視器鎖為了:進(jìn)入同步代碼塊/方法或者被調(diào)用后重	  		 * 新進(jìn)入同步代碼/方法
     */
    BLOCKED,
    /**
     * 線程等待狀態(tài),一個線程處于等待狀態(tài)由于調(diào)用了以下這幾種方法:Object.wait;Thread.join;LockSupp
     * ort.park。處于等待的線程正在等待另一個線程執(zhí)行一個特定的操作。
     */
    WAITING,
    /**
     * 線程超時等待狀態(tài),一個線程處于超時等待狀態(tài)在一個特定的等待時間,由于調(diào)用了以下幾個方法Thread.slee
     * p;Object.wait(long);Thread.join(long);LockSupport.parkNanos;LockSupport.parkUntil。
     */
    TIMED_WAITING,
    /**
     * 線程結(jié)束狀態(tài),線程已經(jīng)執(zhí)行完成了。
     */
    TERMINATED;
}

線程狀態(tài)轉(zhuǎn)換

線程狀態(tài)轉(zhuǎn)換圖

線程從創(chuàng)建后就在幾個狀態(tài)中切換。下面是一個線程狀態(tài)轉(zhuǎn)換圖,調(diào)用不同的方法就可以切換線程線程的狀態(tài)。

Java線程的基礎(chǔ)用法教程

運(yùn)行狀態(tài)&無限等待

調(diào)用Object.wait();Thread.join();LockSupport.park()方法可以讓線程從運(yùn)行狀態(tài)進(jìn)入到無限等待狀態(tài)。

  • wait方法

    是屬于Object類的,對象調(diào)用wait方法后會讓當(dāng)前持有對象鎖的線程釋放當(dāng)前對象鎖并進(jìn)入等待隊(duì)列。對象調(diào)用notify從等待隊(duì)列隨機(jī)選擇一個線程喚醒去競爭對象鎖,對象調(diào)用notifyall會喚醒等待隊(duì)列中的所有線程去競爭對象鎖。

    public class Demo {
        public static void main(String[] args) {
            Demo demo = new Demo();
            Thread t1 = new Thread(() -> {
                synchronized (demo) {
                    System.out.println("t1 start");
                    try {
                        demo.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("t1 end");
                }
            });
            Thread t2 = new Thread(() -> {
               synchronized (demo) {
                   System.out.println("t2 start");
                   System.out.println("t2 end");
                   demo.notify();
               }
            });
            t1.start();
            t2.start();
        }
    }


  • join方法

    是屬于Thread類的,join方法是阻塞調(diào)用此方法的線程,當(dāng)線程a調(diào)用線程b的b.join(long),線程a會阻塞直到線程b執(zhí)行完成。

    public class Demo {
        public static void main(String[] args) throws Exception {
          	System.out.println("main start");
            Thread t1 = new Thread(() -> {
                System.out.println("t1 start");
                System.out.println("t1 end");
            });
            t1.start();
            t1.join();
            System.out.println("main end");
        }
    }


  • park方法

    是屬于LockSupport類的,LockSupport是一個線程阻塞工具類,所有的方法都是靜態(tài)方法,可以使用park方法來阻塞線程,使用unpart來喚醒線程。

    public class Demo {
        public static void main(String[] args) {
            System.out.println("main start");
            Thread t1 = new Thread(() -> {
                System.out.println("t1 start");
                LockSupport.park();
                System.out.println("t1 end");
            });
            t1.start();
            LockSupport.unpark(t1);
            System.out.println("main end");
        }
    }


運(yùn)行狀態(tài)&超時等待

調(diào)用Object.wait(long);Thread.join(long);LockSupport.park(long)方法可以讓線程從運(yùn)行狀態(tài)進(jìn)入到等待狀態(tài),直到到達(dá)等待時間或者主動喚醒。

  • wait(long)方法

    是屬于Object類的,當(dāng)對象調(diào)用wait(long)后會讓當(dāng)前持有對象鎖的線程釋放掉當(dāng)前對象鎖進(jìn)入等待隊(duì)列,直到到達(dá)等待時間或者對象調(diào)用notify或者notifyall從等待隊(duì)列中喚醒線程,線程又重新開始競爭鎖。

public class Demo {
    public static void main(String[] args) {
        Demo demo = new Demo();
        Thread t1 = new Thread(() -> {
            synchronized (demo) {
                for (int i = 0; i < 1000; i++) {
                    if (i == 500) {
                        try {
                            demo.wait(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("------t1------: " + i);
                }
            }
        });
        Thread t2 = new Thread(() -> {
            synchronized (demo) {
                for (int i = 0; i < 1000; i++) {
                    System.out.println("------t2------: " + i);
                }
            }
        });
        t1.start();
        t2.start();
    }
}
  • join(long)方法

    是屬于Thread類的,join(long)方法是阻塞調(diào)用此方法的線程,當(dāng)線程a調(diào)用線程b的b.join(long),線程a會阻塞直到到達(dá)阻塞時間或者線程b執(zhí)行完成。

public class Demo {
    public static void main(String[] args) throws Exception {
        System.out.println("main start");
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                System.out.println("----t1----: " + i);
            }
        });
        t1.start();
        t1.join(1);
        System.out.println("main end");
    }
}
  • parkUntil(long)和parkNanos(long)

    是屬于LockSupport類的,LockSupport是一個線程阻塞工具類,所有的方法都是靜態(tài)方法,可以使用parkUntil(long)和parkNanos(long)方法來阻塞線程。parkNanons是阻塞long時間,parkUntil是阻塞截止到long時間。

public class Demo {
    public static void main(String[] args) {
        System.out.println("main start");
        Thread t1 = new Thread(() -> {
            System.out.println("t1 start");
            LockSupport.parkNanos(3000000000L);
            System.out.println("t1 end");
        });
        t1.start();
        System.out.println("main end");
    }
}
public class Demo {
    public static void main(String[] args) throws Exception{
        System.out.println("main start");
        Thread t1 = new Thread(() -> {
            System.out.println("t1 start");
            String dateTimeStr = "2021-04-04 14:57:00";
            DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            LocalDateTime dateTime = LocalDateTime.parse(dateTimeStr, df);
            LockSupport.parkUntil(dateTime.toInstant(ZoneOffset.of("+8")).toEpochMilli());
            System.out.println("t1 end");
        });
        t1.start();
        System.out.println("main end");
    }
}

無限等待&阻塞狀態(tài)

對象調(diào)用wait方法后線程會進(jìn)入無限等待狀態(tài),當(dāng)對象調(diào)用notify或者notifyAll時,線程將從無限等待狀態(tài)進(jìn)入阻塞狀態(tài)。

阻塞狀態(tài)到運(yùn)行狀態(tài)

線程處于阻塞狀態(tài),如果獲取到鎖對象,就進(jìn)入運(yùn)行狀態(tài)。

感謝各位的閱讀,以上就是“Java線程的基礎(chǔ)用法教程”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Java線程的基礎(chǔ)用法教程這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI