您好,登錄后才能下訂單哦!
這篇文章主要介紹“怎么創建Thread類”,在日常操作中,相信很多人在怎么創建Thread類問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么創建Thread類”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
進程(Process)是具有一定獨立功能的程序關于某個數據集合上的一次運行活動,是系統進行資源分配和調度的一個獨立單位。程序只是一組指令的有序集合,它本身沒有任何運行的含義,只是一個靜態實體。而進程則不同,它是程序在某個數據集上的執行,是一個動態實體。它因創建而產生,因調度而運行,因等待資源或事件而被處于等待狀態,因完成任務而被撤消,反映了一個程序在一定的數據集上運行的全部動態過程。
線程(Thread)是進程的一個實體,是CPU調度和分派的基本單位。線程不能夠獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。
線程和進程的關系是:線程是屬于進程的,線程運行在進程空間內,同一進程所產生的線程共享同一內存空間,當進程退出時該進程所產生的線程都會被強制退出并清除。線程可與屬于同一進程的其它線程共享進程所擁有的全部資源,但是其本身基本上不擁有系統資源,只擁有一點在運行中必不可少的信息(如程序計數器、一組寄存器和棧)。
要理解多線程的概念,就要首先明白它所帶來的好處,假設沒有線程的概念存在,那么當一個應用程序在執行時,如果需要多個分支程序能夠同時處理數據,就需要再開啟一個進程,
例如:要想同時執行多個JAVA文件的main方法,那豈不是要啟動多個JAVA虛擬機才可以?每個虛擬機都要為它分配一塊內存,計數器、寄存器等等,這樣消耗系統資源的話,操作系統是肯定不樂意的!為了能夠同時運行多個分支程序又不至于大動干戈,所以才有了線程的概念,線程的開啟相對來說是較容易的,它是在進程內完成的。操作系統不會再給它分配一塊內存,數據區等,所以說一個進程當中的所有線程都是共享內存的。
由于現在的操作系統都是支持多進程和多線程的。(早先的電腦例如DOS系統是不支持多進程的,就是說同一時間只能有一個程序在運行)而CPU的數量永遠是有限的,雖然隨著硬件的發展,一臺電腦的CPU數量變的越來越多,但永遠也無法達到每一個程序分配一個CPU的程度。因此操作系統必須要將有限的CPU資源對應用程序進行合理的分配。也就是說,多個程序搶一個CPU的話,大家就要輪流執行,也就存在了進程之間的切換,同樣多個線程之間也需要切換。線程之間的切換相對進程來說開銷是比較小的。所以它被認為是輕量級的。
JDK中提供了一個Thread類,它可以幫助我們在java程序中額外啟動一個線程。為什么說是額外?因為main函數也是一個線程,它稱之為程序的主線程。
啟動一個線程,當然就需要創建Thread類的一個實例:
Thread t = new Thread(){ @Override publicvoid run() { // TODO Auto-generated method stub } }; ---------------------------------------------------------------- Thread t = new Thread(new Runnable(){ @Override publicvoid run() { // TODO Auto-generated method stub } }); t.start(); |
Thread類接收一個參數,一個Runnable類型的對象,查看API文檔得知,Runnable是一個接口,并且定義了一個run()方法。
你只需要自定義一個類實現該接口,將你需要執行的代碼,寫在run方法里就可以了。除此以外,開啟線程還有另外一個辦法,那就是自定義一個類,繼承Thread類。再觀察API文檔,我們看到Thread類本身也實現了Runnable接口,所以我們把Thread類中的run方法進行重寫,也可以達到目的。
線程的睡眠,睡眠時不占用CPU資源,可以被interrupt()方法打斷,打斷后線程會拋出InterruptedException,線程在睡眠的過程中,所拿到的同步鎖是不會釋放的。
當線程調用join方法時,該線程會與當前線程進行合并,即等待該線程執行完成,再繼續執行,join還有帶參數的重載方法,可以指定等待多少毫秒。
若在main方法中執行 t.join(2000); main線程會等待線程t兩秒鐘,之后再運行。
該方法使線程讓出CPU資源,由運行狀態轉為就緒狀態。此時操作系統會重新為線程分配CPU。并且yield()方法只能讓同優先級的線程有執行的機會。
yield不會導致線程阻塞,所以無法保證一定能將CPU讓給別的線程。假設某個線程的優先級別最高,此時調用yield方法,操作系統很有可能再次選中該線程并分配CPU。就好像一個年齡最大的人讓出自己最年長的稱號讓大家重新選出最年長者一樣。這種假惺惺的高風亮節是不會獲得大家好感的。
線程的優先級用數字來表示,從1~10表示優先級由低到高。操作系統在調度CPU的時候,會優先把CPU分配給優先級高的線程來運行。
銀行取款,數據的并發操作
ATM |
銀行柜臺 |
數據庫 |
查詢請求1 |
返回結果1 |
取款請求1 |
查詢請求2 |
返回結果2 |
取款請求2 |
查詢請求1和查詢請求2都執行完畢的時候,假如數據庫返回的結果1和結果2都是余額為10000。此時銀行柜臺發出取款請求1,取款8000元,由于余額10000>8000,因此請求被允許,余額應剩余2000。取款請求最終轉換為DATABASE的更新操作,將余額更新為2000. 在同一時刻,ATM發出取款請求2,取款8000元,由于余額10000>8000,因此請求再次被允許,余額應剩余2000。取款請求最終同樣轉換為數據庫更新操作,將余額更新為2000。這個時候就出現了一個嚴重的問題,共取走了16000,數據庫卻還剩2000。
這是一個非常經典的多線程引發的安全問題,為了解決這個問題,我們引入同步鎖的概念。
同步鎖的概念:所謂同步,指的是按步驟依次執行。如果是同時執行的操作,我們稱為異步或者叫并發。當一個線程執行請求時,如果把數據庫的一行記錄鎖定,其它線程此時不能操作數據庫,必須等待第一個線程結束。這樣就能避免同時操作一行記錄所帶來的問題。
線程安全問題:是指當多個線程訪問同一個數據時,如果每個線程都對該數據做了修改,那么該數據的值就會變得難以確定。一個值不能確定的變量,有可能會引發整個系統產生災難性的錯誤,所以這是我們絕不希望看到的。因此,解決線程安全問題,兩種辦法:
第一、如果一個類需要被多線程訪問,那么絕對不要添加任何成員變量,防止多線程共享一份數據而引發的數據混亂。
第二、采用線程同步來訪問同一個數據。
JAVA為了解決線程安全問題,也引入了同步鎖的機制: Synchronized關鍵字
JVM的規范中這樣寫道:
“在JVM中,每個對象和類在邏輯上都是和一個監視器相關聯的”
“為了實現監視器的排他性監視能力,JVM為每一個對象和類都關聯一個鎖”
這個也叫互斥鎖,就說指多個線程同時來獲取對象的鎖,監視器負責監管對象的鎖,一次只允許一個線程拿到鎖。采用這樣的方法,使得多個線程可以同步順序執行。
Synchronized關鍵字的四種用法:
public synchronized void someMethod() { //方法體 } 該方法被聲明為同步方法,也就是說執行該方法必須獲得當前對象鎖 |
public synchronized static void someMethod() { //方法體 } 該方法被聲明為同步方法,由于方法為靜態的,因此無法獲得當前對象的鎖,所以這個方法獲得的是當前類的class對象鎖,也就是說執行該方法必須先獲得這個類的class對象鎖 |
public static void someMethod() { synchronized(SynTest.class){ //方法體 } } 該方法包含同步代碼塊,也就是說執行syn語句塊的代碼前必須先獲得當前類的class對象鎖。 |
public void someMethod() { synchronized(this){ //方法體 } } 該方法包含同步代碼塊,也就是說執行syn語句塊的代碼前必須先獲得當前對象鎖 |
synchronized用于指定同步代碼塊,并且它只能鎖定對象,無法鎖定基本數據類型。
死鎖,既永遠也解不開的鎖。在程序開發中我們應極力避免這種問題。
當線程1鎖定了資源A,同時線程2鎖定了資源B,這是線程1必須拿到資源B的鎖,才能執行結束,從而釋放資源A。而線程2必須拿到資源A,才能釋放資源B。
形成這樣的僵局,兩個線程就無法結束,造成死鎖。
關于死鎖的例子程序:
publicclass DeadLock { publicstaticvoid main(String[] args) { Thread t1 = new Thread(new DL(true)); Thread t2 = new Thread(new DL(false)); t1.start(); t2.start(); } } class DL implements Runnable { static Object lockX = new Object(); static Object lockY = new Object(); booleanflag = true; public DL(boolean flag) { this.flag = flag; } @Override publicvoid run() { if(flag) { synchronized(lockX) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(lockY) {//阻塞 System.out.println("執行結束"); } } } else { synchronized(lockY) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(lockX) { System.out.println("執行結束"); } } } } } |
到此,關于“怎么創建Thread類”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。