您好,登錄后才能下訂單哦!
這篇文章主要講解了“Java線程的基礎(chǔ)用法教程”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Java線程的基礎(chǔ)用法教程”吧!
進(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)建一個線程類有三種方式:
繼承Thread類
實(shí)現(xiàn)Runnable接口
實(shí)現(xiàn)Callable接口
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是提供線程的接口,有一個抽象方法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(); } }
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(); } } }
在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; }
線程從創(chuàng)建后就在幾個狀態(tài)中切換。下面是一個線程狀態(tài)轉(zhuǎn)換圖,調(diào)用不同的方法就可以切換線程線程的狀態(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"); } }
調(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"); } }
對象調(diào)用wait方法后線程會進(jìn)入無限等待狀態(tài),當(dāng)對象調(diào)用notify或者notifyAll時,線程將從無限等待狀態(tài)進(jì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)注!
免責(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)容。