<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
一個Java 程式從main() 方法開始執行,然後按照既定的程式碼邏輯執行,看似沒有其他執行緒參與,但實際上Java
程式天生就是多執行緒程式,因為執行main() 方法的是一個名稱為main 的執行緒。
public static void main(String[] args) { // java 虛擬機器器執行緒系統的管理介面 ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); // 不需要獲取同步的monitor 和synchronizer 資訊,僅僅獲取執行緒和執行緒堆疊資訊 ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false); // 遍歷執行緒,僅列印執行緒ID 和執行緒名稱資訊 for (ThreadInfo threadInfo : threadInfos) { System.out.println("執行緒ID" + threadInfo.getThreadId() + "執行緒名" + threadInfo.getThreadName()); } }
上面程式碼輸出的結果:
從上面的例子中,我們能發現,在Java中短短的幾行程式碼,就給我們啟動了5個執行緒,當然,不同的版本,啟動的執行緒數量也不一樣,由此我們可以得出:**Java
天生就是多執行緒的**
執行緒的啟動方式有兩種(原始碼中的註釋是這麼寫的)參見程式碼:cn.enjoyedu.ch1.base.NewThread:
範例程式碼:(派生自Thread 類,來實現我們的兩種執行緒啟動方式)
/** * 擴充套件自Thread 類 */ private static class UserThread extends Thread{ @Override public void run() { System.out.println("UserThread.run"); } } /** * 擴充套件自 Runnable 類 */ private static class UserRunnable implements Runnable { @Override public void run() { System.out.println("UserRunnable.run"); } } public static void main(String[] args) { UserThread userThread = new UserThread(); userThread.start(); UserRunnable userRunnable = new UserRunnable(); new Thread(userRunnable).start(); }
Thread 和 Runnable 的區別:
Thread
是Java 裡對執行緒的唯一抽象。Runnable
是Java對任務(業務邏輯)的抽象。Thread
可以接受任意一個Runnable
的範例並執行。執行緒自然終止:
要麼是run 執行完成了,要麼是丟擲了一個未處理的異常導致執行緒提前結束。stop:
暫停、恢復和停止操作對應線上程Thread的API就是suspend()、resume() 和 stop()
。但是這些API都是過期的,不再建議使用。不建議使用的主要原因有:以suspend()
方法為例,在呼叫後,執行緒不會釋放已佔有的資源(比如鎖),而是佔有資源進入睡眠狀態,這樣容易引發死鎖問題。同樣,stop()
方法在終結一個執行緒時,不會保證執行緒的資源正常釋放,通常是沒有給予執行緒完成資源釋放的機會,因此會到導致程式可能工作在不確定的狀態下。整因為suspend()、resume() 和 stop()
方法帶來的副作用,這些方法才會被標註為不建議使用的過期方法中。中斷:
安全的中止則是其它執行緒通過呼叫執行緒A的interrupt()
方法對其進行中止操作,中斷代表著其它執行緒對A執行緒打了個招呼,“A, 你要中斷了”,不代表執行緒A 會立即停止自己的工作,同樣A執行緒可以不理會這種請求。因為Java 中的執行緒是共同作業式的,不是搶佔式。執行緒通過檢查自身的中斷標誌位是否被置為true來進行響應。private static class UserThread extends Thread{ public UserThread(String name){ super(name); } @Override public void run() { String threadName = Thread.currentThread().getName(); System.out.println(threadName + "interrupt flag = " + isInterrupted()); while (!isInterrupted()){ // while (!Thread.interrupted()){ // while (true){ System.out.println(threadName+ "is running"); System.out.println(threadName+ "inner interrupt flag = "+ isInterrupted()); } System.out.println(threadName+ "interrupt flag = " + isInterrupted()); } } public static void main(String[] args) { Thread endTread = new UserThread("endTread"); endTread.start(); try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } // 中斷執行緒, 其實設定執行緒的標識位 endTread.interrupt(); }
執行上面的程式碼:
我們發現,在使用isInterrupted()
進行執行緒中斷的之後,isInterrupted()
會返回一個true。
我們一起來看一下isInterrupted() 方法的原始碼:
我們再使用一下靜態的 interrupted()
方法,他返回的也是一個bool 值,
先看一下這個方法的原始碼:
從原始碼中我們發現,它返回也是中斷識別符號,但是,它把中斷識別符號給重新賦值成了true。
我們來看一下執行效果:
由此我們可以總結出:**執行緒通過方法 isInterrupted()
來進行判斷是否被中斷,也可以呼叫靜態方法 Thread.interrupted()
來進行判斷當前執行緒是否被中斷,不過 Thread.interrupted()
會同時將中斷標識位改寫為 false
。**
如果一個執行緒處於阻塞狀態(如執行緒呼叫了 thread.sleep、thread.join、 thread.wait 等)
,則執行緒在檢查中斷標識時,如果發現中斷標識位true
,則會在這些阻塞方法呼叫處丟擲InterruptedException
異常,並且在丟擲異常後會立即將執行緒的中斷標識位清除,即重新設定為true
。
private static class UserThread extends Thread{ public UserThread(String name){ super(name); } @Override public void run() { String threadName = Thread.currentThread().getName(); System.out.println(threadName + "interrupt flag = " + isInterrupted()); while (!isInterrupted()){ try { Thread.sleep(100); } catch (InterruptedException e) { System.out.println(threadName+ "inner interrupt flag = "+ isInterrupted()); e.printStackTrace(); } System.out.println(threadName+ "is running"); } System.out.println(threadName+ "interrupt flag = " + isInterrupted()); } } public static void main(String[] args) { Thread endTread = new UserThread("endTread"); endTread.start(); try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } // 中斷執行緒, 其實設定執行緒的標識位 endTread.interrupt(); }
上面程式碼執行結果:
那麼像這種,我們該怎麼去中中斷操作呢?只需要在??catch?
? 中呼叫??interrupt()?
? 方法就可以了
程式碼執行結果:
?new Thread()?
? 其實只是new出一個thread的範例,還沒有和作業系統中真正的執行緒掛起勾來。只有執行了??start()?
? 方法後,才實現了真正意義上的啟動執行緒。?start()?
? 方法讓一個執行緒進入就緒佇列等待分配CPU,分到CPU後才呼叫??run()?
?方法,??start()?
? 方法不能重複呼叫,如果重複呼叫,就會丟擲異常。那麼start() 和 run() 有什麼區別呢?請看程式碼:
private static class UserThread extends Thread{ @Override public void run() { int i = 90; while (i > 0){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("I am "+Thread.currentThread().getName()+"and now the i="+ i--); } } } public static void main(String[] args) { Thread endTread = new UserThread(); endTread.setName("threadRun"); endTread.start(); }
程式碼執行結果:(觀察執行結果,我們可以得出,呼叫start() 方法的時候,執行start() 方法的是子執行緒)
我們修改一下程式碼,呼叫??run()?
? 方法
public static void main(String[] args) { Thread endTread = new UserThread(); endTread.setName("threadRun"); endTread.run(); }
檢視執行結果:(觀察執行結果,我們可以得出,呼叫run() 方法的時候,執行run() 方法的是主執行緒)
join() 方法是把指定的執行緒加入到當前執行緒,可以將兩個交替執行的執行緒合併為順序執行。
static class Students implements Runnable { private Thread thread; public Students(Thread thread) { this.thread = thread; } public Students() { } @Override public void run() { System.out.println("學生開始排隊打飯。。。。。"); try { if (thread != null) thread.join(); // 休眠2 秒 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("學生打飯完成"); } } static class Teacher implements Runnable { @Override public void run() { try { // 休眠2 秒 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("老師開始打飯、、、、、"); System.out.println(Thread.currentThread().getName() + "老師打飯完成。"); } } public static void main(String[] args) throws InterruptedException { Teacher teacher = new Teacher(); Thread teaThread = new Thread(teacher); Students students = new Students(teaThread); Thread stuThread = new Thread(students); stuThread.start(); teaThread.start(); System.out.println("我開始打飯、、、、、"); stuThread.join(); // 讓主執行緒休眠2 秒 Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"我打飯完成"); }
程式碼執行結果如下:
由上程式碼執行結果,我們可以得出:**線上程B中呼叫了執行緒A的??join()?
? 方法,只到執行緒A 執行完畢後,才會繼續執行執行緒B的。**
在 Java 執行緒中,通過一個整型成員變數 priority 來控制優先順序,優先順序的範 圍從 1~10,線上程構建的時候可以通過??setPriority(int)?
?方法來修改優先順序,預設 優先順序是 5,優先順序高的執行緒分配時間片的數量要多於優先順序低的執行緒。
設定執行緒優先順序時,針對頻繁阻塞(休眠或者I/O操作)的執行緒需要設定較 高優先順序,而偏重計算(需要較多CPU時間或者偏運算)的執行緒則設定較低的 優先順序,確保處理器不會被獨佔。在不同的 JVM 以及作業系統上,執行緒規劃會 存在差異,有些作業系統甚至會忽略對執行緒優先順序的設定。
Daemon(守護)執行緒是一種支援型執行緒,因為它主要被用作程式中後臺調 度以及支援性工作。這意味著,當一個 Java 虛擬機器器中不存在非 Daemon 執行緒的 時候,Java 虛擬機器器將會退出。可以通過呼叫??Thread.setDaemon(true)?
?將執行緒設定 為Daemon執行緒。我們一般用不上,比如垃圾回收執行緒就是Daemon執行緒。
Daemon執行緒被用作完成支援性工作,但是在 Java 虛擬機器器退出時Daemon線 程中的??finally?
? 塊並不一定會執行。在構建Daemon執行緒時,不能依靠??finally?
? 塊中 的內容來確保執行關閉或清理資源的邏輯。
執行緒開始執行,擁有自己的棧空間,就如同一個指令碼一樣,按照既定的程式碼 一步一步地執行,直到終止。但是,每個執行中的執行緒,如果僅僅是孤立地執行, 那麼沒有一點兒價值,或者說價值很少,如果多個執行緒能夠相互配合完成工作, 包括資料之間的共用,協同處理事情。這將會帶來巨大的價值。
Java支援多個執行緒同時存取一個物件或者物件的成員變數,關鍵字synchronized可以修飾方法或者以同步塊的形式來進行使用,它主要確保多個線 程在同一個時刻,只能有一個執行緒處於方法或者同步塊中,它保證了執行緒對變數 存取的可見性和排他性,又稱為內建鎖機制。
下面我們看一段程式碼,在main 方法中啟動兩個執行緒,每個執行緒的count 值都是10000,兩個執行緒的和應該就是20000。
public class OnlyMain { private long count = 0; private Object object = new Object(); public long getCount() { return count; } public void incCount() { count++; } // 執行緒 private static class Count extends Thread { private OnlyMain onlyMain; public Count(OnlyMain onlyMain) { this.onlyMain = onlyMain; } @Override public void run() { for (int i = 0; i < 10000; i++) { // count = count++ = 10000 onlyMain.incCount(); } } } public static void main(String[] args) throws InterruptedException { OnlyMain onlyMain = new OnlyMain(); // 啟動兩個執行緒 Count count1 = new Count(onlyMain); Count count2 = new Count(onlyMain); count1.start(); count2.start(); Thread.sleep(50); System.out.println(onlyMain.count); } }
程式碼的執行結果是:
經過多次執行,每次執行的結果都不一樣,只有在很少很少的機率的情況下,才會出現正確的20000結果值,這是為什麼呢?
這是因為,兩個執行緒同時對count 成員變數進行存取,才導致輸出結果的錯誤。怎麼解決呢?使用synchronized 內建鎖。
修改上面程式碼中的incCount() 方法,新增一個內鎖:
public synchronized void incCount() { count++; }
這樣我們就能保證每次執行的正確結果了:
物件鎖是用於物件實體方法的鎖,或者一個物件範例上,類鎖 是用於類的靜態方法或一個類的class 上的,我們知道,類的物件範例可以有很多個,但是每個類只有一個class物件,所有不同物件範例的物件鎖是互不干擾的,但是每個類只有一個類鎖。
但是有一點必須要注意的是,其實類鎖只是一個概念上的東西,並不是真實存在的,類鎖其實鎖的是每個類的對應的class 物件。類鎖和物件鎖之間也是互不干擾的。
到此這篇關於Java 天生就是多執行緒的文章就介紹到這了,更多相關Java 多執行緒內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45