標籤:決定 請求 相關 範圍 分享圖片 isp 結束 java線程 正在執行
多線程 進程:每個進程都是獨立的代碼和資料空間(進程上下文),進程間的切換會有較大的開銷,一個進程包含1-n個線程。(進程是資源分派的最小單位)線程:是運行在一個單一進程上下文中的邏輯流,由核心進行調度。同一類線程共用代碼和資料空間,每個線程有獨立的運行棧和程式計數器(PC),線程切換開銷小。(線程是CPU調度的最小單位)
線程的建立與啟動
實現多線程的兩種方式:1.實現Runnable介面(推薦)2.繼承Thread
import java.io.File;/** * 啟動一個線程,每隔一秒掃描一個指定檔案夾,發現裡面有其他檔案存在就刪除,保證該檔案夾下只能存在(*.txt)的檔案。 * @author dong007 * */public class ThreadTxt extends Thread { /** 指定檔案夾路徑 */ private static String PATH = ""; public void run() { File file = new File(PATH); File[] fileArray = file.listFiles(); while (true) { try { Thread.sleep(1000); // 休眠一秒 } catch (InterruptedException e) { e.printStackTrace(); } for (File currFile : fileArray) { if (currFile.isFile() && !((currFile.getName()).endsWith(".txt"))) { currFile.delete(); System.out.println("此檔案夾下非*.txt檔案已刪除"); } else if(currFile.isDirectory()){ System.out.println("此檔案夾下不存在檔案"); } else{ System.out.println("結束"); } } } }}View Code
import java.io.File;public class MonitorRunner implements Runnable { private static String PATH = ""; public void run() { File file = new File(PATH); File[] fileArray = file.listFiles(); int before = 0; int current = 0; while(true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } before = current; current = fileArray.length; int count = current - before; if (count == 0) { System.out.println("未進行任何操作"); } else if (count > 0) { System.out.println("建立檔案操作"); } else { System.out.println("刪除檔案操作"); } } }}View Coderun()方法中寫需要線上程中執行的代碼;Thread.currentThread();在main方法中建立一個Thread的執行個體;將Runnable的執行個體傳入Thread的構造器調用Thread執行個體的.start方法
public class Main { public static void main(String[] args) { ThreadTxt threadTxt = new ThreadTxt(); threadTxt.start(); MonitorRunner monitorRunner = new MonitorRunner(); Thread monitorThread = new Thread(monitorRunner); monitorThread.start(); }}View Code
線程的6種狀態:
通過調用當前線程執行個體的getState()方法,可返回目前狀態 ? New (新建立)Thread t=new Thread(r); ? Runnable(可運行)t.start(); ? Blocked(被阻塞)當一個線程試圖擷取一個內部的對象鎖,而該鎖被其他線程持有,則該線程進入阻塞狀態。 ? Waiting(等待)當線程等待另一個線程通知調度器一個條件時,它自己進入等待狀態。如調用Object.wait方法或Thread.join方法。 ? Timed waiting(計時等待)有幾個方法有一個逾時參數。調用它們導致線程進入計時等待狀態。這一狀態將一直保持到逾時期滿或者接收到適當的通知。如Thread.sleep,Object.wait,Thread.join等 ? Terminated(被終止)線程因如下兩個原因之一而被終止: 1. 因為run方法正常退出而自然死亡; 2. 因為一個沒有捕獲的異常終止了run方法而意外死亡
在一些書和文章中,將線程運行分為五個階段:建立、就緒、運行、阻塞、終止。1、建立狀態(New):新建立了一個線程對象。2、就緒狀態(Runnable):線程對象建立後,其他線程調用了該對象的start()方法。該狀態的線程位於可運行線程池中,變得可運行,等待擷取CPU的使用權。3、運行狀態(Running):就緒狀態的線程擷取了CPU,執行程式代碼。4、阻塞狀態(Blocked):阻塞狀態是線程因為某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。阻塞的情況分三種:(一)、等待阻塞:啟動並執行線程執行wait()方法,JVM會把該線程放入等待池中。(wait會釋放持有的鎖)(二)、同步阻塞:啟動並執行線程在擷取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入鎖池中。(三)、其他阻塞:啟動並執行線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀態。當sleep()狀態逾時、join()等待線程終止或者逾時、或者I/O處理完畢時,線程重新轉入就緒狀態。(注意,sleep是不會釋放持有的鎖)5、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。線程的調度1、調整線程優先順序:Java線程有優先順序,優先順序高的線程會獲得較多的運行機會。Java線程的優先順序用整數表示,取值範圍是1~10,Thread類有以下三個靜態常量:static int MAX_PRIORITY 線程可以具有的最高優先順序,取值為10。 static int MIN_PRIORITY 線程可以具有的最低優先順序,取值為1。 static int NORM_PRIORITY 分配給線程的預設優先順序,取值為5。 Thread類的setPriority()和getPriority()方法分別用來設定和擷取線程的優先順序。 每個線程都有預設的優先順序。主線程的預設優先順序為Thread.NORM_PRIORITY。線程的優先順序有繼承關係,比如A線程中建立了B線程,那麼B將和A具有相同的優先順序。JVM提供了10個線程優先順序,但與常見的作業系統都不能很好的映射。如果希望程式能移植到各個作業系統中,應該僅僅使用Thread類有以下三個靜態常量作為優先順序,這樣能保證同樣的優先順序採用了同樣的調度方式。 2、線程睡眠:Thread.sleep(long millis)方法,使線程轉到阻塞狀態。millis參數設定睡眠的時間,以毫秒為單位。當睡眠結束後,就轉為就緒(Runnable)狀態。sleep()平台移植性好。3、線程等待:Object類中的wait()方法,導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 喚醒方法。這個兩個喚醒方法也是Object類中的方法,行為等價於調用 wait(0) 一樣。4、線程讓步:Thread.yield() 方法,暫停當前正在執行的線程對象,把執行機會讓給相同或者更高優先順序的線程。5、線程加入:join()方法,等待其他線程終止。在當前線程中調用另一個線程的join()方法,則當前線程轉入阻塞狀態,直到另一個進程運行結束,當前線程再由阻塞轉為就緒狀態。6、線程喚醒:Object類中的notify()方法,喚醒在此對象監視器上等待的單個線程。如果所有線程都在此對象上等待,則會選擇喚醒其中一個線程。選擇是任意性的,並在對實現做出決定時發生。線程通過調用其中一個 wait 方法,在對象的監視器上等待。 直到當前的線程放棄此對象上的鎖定,才能繼續執行被喚醒的線程。被喚醒的線程將以常規方式與在該對象上主動同步的其他所有線程進行競爭;例如,喚醒的線程在作為鎖定此對象的下一個線程方面沒有可靠的特權或劣勢。類似的方法還有一個notifyAll(),喚醒在此對象監視器上等待的所有線程。 注意:Thread中suspend()和resume()兩個方法在JDK1.5中已經廢除,不再介紹。因為有容易導致鎖死的。 sleep()和yield()的區別 sleep()和yield()的區別):sleep()使當前線程進入停滯狀態,所以執行sleep()的線程在指定的時間內肯定不會被執行;yield()只是使當前線程重新回到可執行狀態,所以執行yield()的線程有可能在進入到可執行狀態後馬上又被執行。 sleep 方法使當前運行中的線程睡眼一段時間,進入不可運行狀態,這段時間的長短是由程式設定的,yield 方法使當前線程讓出 CPU 佔有權,但讓出的時間是不可設定的。實際上,yield()方法對應了如下操作:先檢測當前是否有相同優先順序的線程處於同可運行狀態,如有,則把 CPU 的佔有權交給此線程,否則,繼續運行原來的線程。所以yield()方法稱為“退讓”,它把運行機會讓給了同等優先順序的其他線程 另外,sleep 方法允許較低優先順序的線程獲得運行機會,但 yield() 方法執行時,當前線程仍處在可運行狀態,所以,不可能讓出較低優先順序的線程些時獲得 CPU 佔有權。在一個運行系統中,如果較高優先順序的線程沒有調用 sleep 方法,又沒有受到 I\O 阻塞,那麼,較低優先順序線程只能等待所有較高優先順序的線程運行結束,才有機會運行。 線程同步1、線程同步的目的是為了保護多個線程訪問一個資源時對資源的破壞。2、線程同步方法是通過鎖來實現,每個對象都有且僅有一個鎖,這個鎖與一個特定的對象關聯,線程一旦擷取了對象鎖,其他訪問該對象的線程就無法再訪問該對象的其他非同步方法。3、對於靜態同步方法,鎖是針對這個類的,鎖對象是該類的Class對象。靜態和非靜態方法的鎖互不干預。一個線程獲得鎖,當在一個同步方法中訪問另外對象上的同步方法時,會擷取這兩個對象鎖。4、對於同步,要時刻清醒在哪個對象上同步,這是關鍵。5、編寫安全執行緒的類,需要時刻注意對多個線程競爭訪問資源的邏輯和安全做出正確的判斷,對“原子”操作做出分析,並保證原子操作期間別的線程無法訪問競爭資源。6、當多個線程等待一個對象鎖時,沒有擷取到鎖的線程將發生阻塞。7、死結是線程間相互等待鎖鎖造成的,在實際中發生的機率非常的小。但是,一旦程式發生死結,程式將死掉。1、synchronized關鍵字的範圍有二種: 1)是某個對象執行個體內,synchronized aMethod(){}可以防止多個線程同時訪問 這個對象的synchronized方法(如果一個對象有多個synchronized方法,只要一個線程訪問了其中的一個synchronized方法,其它線程不能同時訪問這個對象中任何一個synchronized方法)。這時,不同的對象執行個體的synchronized方法是不相干擾的。也就是說,其它線程照樣可以同時訪問相同類的另一個對象執行個體中的synchronized方法; 2)是某個類的範圍,synchronized static aStaticMethod{}防止多個線程同時訪問這個類中的synchronized static 方法。它可以對類的所有對象執行個體起作用。 2、除了方法前用synchronized關鍵字,synchronized關鍵字還可以用於方法中的某個區塊中,表示只對這個區塊的資源實行互斥訪問。用法是: synchronized(this){/*區塊*/},它的範圍是當前對象; 3、synchronized關鍵字是不能繼承的,也就是說,基類的方法synchronized f(){} 在繼承類中並不自動是synchronized f(){},而是變成了f(){}。繼承類需要你顯式的指定它的某個方法為synchronized方法; A.無論synchronized關鍵字加在方法上還是對象上,它取得的鎖都是對象,而不是把一段代碼或函數當作鎖――而且同步方法很可能還會被其他線程的對象訪問。B.每個對象只有一個鎖(lock)與之相關聯。C.實現同步是要很大的系統開銷作為代價的,甚至可能造成死結,所以盡量避免無謂的同步控制。註:零長度的byte數組對象建立起來將比任何對象都經濟――查看編譯後的位元組碼:產生零長度的byte[]對象只需3條作業碼,而Object lock = new Object()則需要7行作業碼。
Java基礎之多線程