1. 程式人生 > >Java之線程,常用方法,線程同步,死鎖

Java之線程,常用方法,線程同步,死鎖

時間 imp log 沖突 根據 oms adl 無法 誰的

1, 線程的概念

進程與線程

進程:每個進程都有獨立的代碼和數據空間(進程上下文),進程間的切換會有較大的開銷,一個進程包含1--n個線程。(進程是資源分配的最小單位)

線程:同一類線程共享代碼和數據空間,每個線程有獨立的運行棧和程序計數器(PC),線程切換開銷小。(線程是cpu調度的最小單位)

切換而不是同步
一個程序中的方法有幾條執行路徑, 就有幾個線程

Java中線程的生命周期

技術分享

Java線程具有五中基本狀態

新建狀態(New):當線程對象對創建後,即進入了新建狀態,如:Thread t = new MyThread();

就緒狀態(Runnable):當調用線程對象的start()方法(t.start();),線程即進入就緒狀態。處於就緒狀態的線程,只是說明此線程已經做好了準備,隨時等待CPU調度執行,並不是說執行了t.start()此線程立即就會執行;

運行狀態(Running):當CPU開始調度處於就緒狀態的線程時,此時線程才得以真正執行,即進入到運行狀態。註:就 緒狀態是進入到運行狀態的唯一入口,也就是說,線程要想進入運行狀態執行,首先必須處於就緒狀態中;

阻塞狀態(Blocked):處於運行狀態中的線程由於某種原因,暫時放棄對CPU的使用權,停止執行,此時進入阻塞狀態,直到其進入到就緒狀態,才 有機會再次被CPU調用以進入到運行狀態。根據阻塞產生的原因不同,阻塞狀態又可以分為三種:

1.等待阻塞:運行狀態中的線程執行wait()方法,使本線程進入到等待阻塞狀態;

2.同步阻塞 -- 線程在獲取synchronized同步鎖失敗(因為鎖被其它線程所占用),它會進入同步阻塞狀態;

3.其他阻塞 -- 通過調用線程的sleep()或join()或發出了I/O請求時,線程會進入到阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。

死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。

2, 線程的創建:

Thread類:

java.lang

Class Thread

  • java.lang.Object
    • java.lang.Thread
  • All Implemented Interfaces:
    Runnable
    已知直接子類:
    ForkJoinWorkerThread

    線程是程序中執行的線程。 Java虛擬機允許應用程序同時執行多個執行線程。

    每個線程都有優先權。 具有較高優先級的線程優先於優先級較低的線程執行。 每個線程可能也可能不會被標記為守護程序。 當在某個線程中運行的代碼創建一個新的Thread對象時,新線程的優先級最初設置為等於創建線程的優先級,並且當且僅當創建線程是守護進程時才是守護線程。

    創建一個新的執行線程有兩種方法。 一個是將一個類聲明為Thread的子類。 這個子類應該重寫run類的方法Thread

    另一種方法來創建一個線程是聲明實現類Runnable接口。 那個類然後實現了run方法。 然後可以分配類的實例,在創建Thread時作為參數傳遞,並啟動。

兩種方式:
1, 繼承Thread類
class TestThread extends Thread {......}

 1 package com.hanqi.maya.test;
 2 
 3 import javax.swing.plaf.FontUIResource;
 4 import javax.swing.text.StyledEditorKit.ForegroundAction;
 5 
 6 public class Threadtest {
 7     public static void main(String[] args) {
 8         MyThread mt=new MyThread();
 9         mt.start();//開啟線程 
10         for(int i=0;i<50;i++){
11             System.out.println("main"+i);
12         }
13     }
14 }
15 class MyThread extends Thread{ //通過繼承Thread類的方法來創建多線程
16     
17     public void run(){
18         for(int i=0;i<50;i++){
19             System.out.println("MyThead"+i);
20         }
21     }
22     
23 }

2, 實現Runnable接口, 然後作為參數傳入到Thread類的構造方法中
class TestThread implements Runnable {......}

 1 package com.hanqi.maya.test;
 2 
 3 import javax.swing.plaf.FontUIResource;
 4 import javax.swing.text.StyledEditorKit.ForegroundAction;
 5 
 6 public class Threadtest {
 7     public static void main(String[] args) {
 8         MyThread mt=new MyThread();
 9         
10         Thread t=new Thread(mt);
11         t.start();//自動去找t中實現的Runnable方法
12         for(int i=0;i<50;i++){
13             System.out.println("main"+i);
14         }
15 /*        
16         mt.start();//開啟線程 
17         for(int i=0;i<50;i++){
18             System.out.println("main"+i);
19         }*/
20     }
21 }
22 
23 class MyThread implements Runnable{ //通過繼承Thread類的方法來創建多線程
24     
25     public void run(){
26         for(int i=0;i<50;i++){
27             System.out.println("MyThead"+i);
28         }
29     }
30     
31 }
32 /*class MyThread extends Thread{ //通過繼承Thread類的方法來創建多線程
33     
34     public void run(){
35         for(int i=0;i<50;i++){
36             System.out.println("MyThead"+i);
37         }
38     }
39     
40 }*/

線程的啟動:

調用線程類中的start()方法, 不能直接調用run()方法, 直接調用run()方法是方法調用, 不是啟動線程

3, 線程常用方法

技術分享


isAlive()
判斷線程是否還活著, 調用start()之前和終止之後都是死的, 其他的都是活的

判斷線程是否還在

 1 package com.hanqi.maya.test;
 2 
 3 public class TestThread1 {
 4     public static void main(String[] args) {
 5         MyThread1 mt=new MyThread1();
 6         
 7         Thread t=new Thread(mt);
 8         
 9         System.out.println(t.isAlive());//判斷線程是否還在,此處線程未開啟
10         t.start();
11         System.out.println(t.isAlive());//判斷線程是否還在,此處線程已經開啟
12     }
13 }
14 
15 class MyThread1 implements Runnable{ //通過繼承Thread類的方法來創建多線程
16     
17     public void run(){
18         for(int i=0;i<50;i++){
19             System.out.println("MyThead"+i);
20         }
21     }
22 }

優先級

getPriority()
setPriority()
設置優先級, 優先級的概念: 誰的優先級高, 誰執行的時間就多
Thread裏面的默認優先級:
Thread.MIN_PRIORITY = 1
Thread.MAX_PRIORITY = 10
Thread.NORM_PRIORITY = 5

 1 package com.hanqi.maya.test;
 2 
 3 public class TestPriority {
 4     public static void main(String[] args) {
 5         MyThread11 mt1 = new MyThread11("超人");
 6         MyThread2 mt2 = new MyThread2("蝙蝠俠");
 7         mt1.setPriority(Thread.NORM_PRIORITY + 5);//設置優先級
 8         mt1.start();
 9         mt2.start();
10         System.out.println("123");
11         try {
12             Thread.sleep(2000);
13         } catch (InterruptedException e) {
14             // TODO Auto-generated catch block
15             e.printStackTrace();
16         }
17         System.out.println(456);
18     }
19 }
20 
21 
22 class MyThread11 extends Thread {
23     
24     public MyThread11(String name) {
25         super(name);//調用父類構造方法將name穿減去
26     }
27 
28     public void run() {
29         for (int i = 0; i < 50; i++) {
30             System.out.println(this.getName() + ": " + i);
31         }
32     }
33 }
34 
35 class MyThread2 extends Thread {
36     
37     public MyThread2(String name) {
38         super(name);
39     }
40     
41     public void run() {
42         for (int i = 0; i < 50; i++) {
43             System.out.println(this.getName() + ": " + i);
44         }
45     }
46 }

沈睡和終止

interrupt()
停止線程

這個方法要配合延遲使用,不要直接啟動線程然後直接使用此方法

 1 package interupt;
 2 
 3 import java.util.Date;
 4 
 5 public class TestInterupt {
 6     public static void main(String[] args) {
 7         MyNewThread mnt = new MyNewThread();
 8         Thread t = new Thread(mnt);
 9         t.start();
10         try {
11             Thread.sleep(5000);
12         } catch (InterruptedException e) {
13             e.printStackTrace();
14         }
15         t.interrupt();
16         t.stop();
17 //         終止線程, 將run方法結束, 線程就結束了
18         MyNewThread.flag = false;
19     }
20 }
21 
22 class MyNewThread implements Runnable {
23 
24     static boolean flag = true;
25     @Override
26     public void run() {
27         while(flag) {
28             System.out.println(new Date());
29             try {
30                 Thread.sleep(1000);
31             } catch (Exception e) {
32                 System.out.println("線程終止 !");
33                 System.out.println(e.getMessage());
34                 return;
35             }
36         }
37     }
38     
39 }

Thread.sleep(long millions)將程序暫停一會,暫停當前線程,如果沒有線程也可以給普通程序暫停,需要拋出一個異常即可

 1 package com.hanqi.maya.test;
 2 
 3 import java.util.Date;
 4 
 5 public class TestInterupt {
 6     public static void main(String[] args) {
 7         MyNewThread mnt = new MyNewThread();
 8         Thread t = new Thread(mnt);
 9         t.start();
10         try {
11             Thread.sleep(5000);//先沈睡5s
12         } catch (InterruptedException e) {
13             e.printStackTrace();
14         }
15         /*t.interrupt();//調用會拋出異常
16         t.stop();//不建議使用,已經過時
17 *///         終止線程, 將run方法結束, 線程就結束了
18         MyNewThread.flag = false;//更改屬性終止線程
19     }
20 }
21 
22 class MyNewThread implements Runnable {
23 
24     static boolean flag = true;
25     @Override
26     public void run() {
27         while(flag) {
28             System.out.println(new Date());
29             try {
30                 Thread.sleep(1000);//延遲一秒
31             } catch (Exception e) {
32                 System.out.println("線程終止 !");
33                 System.out.println(e.getMessage());
34                 return;
35             }
36         }
37     }
38     
39 }

join()
合並線程

 1 package join;
 2 
 3 public class TestJoin {
 4     public static void main(String[] args) {
 5         NewThread nt = new NewThread();
 6         nt.start();
 7         try {
 8             nt.join();//合並線程,繼承自Thread,將這個線程加到當前線程,先執行這個線程
 9         } catch (InterruptedException e) {
10             e.printStackTrace();
11         }
12         
13         for (int i = 0; i < 10; i++) {
14             System.out.println("我是main線程-----" + i);
15         }
16     }
17 }
18 
19 class NewThread extends Thread {
20     public void run() {
21         for (int i = 0; i < 10; i++) {
22             System.out.println("我是NewThread線程=====" + i);
23         }
24     }
25 }


yield()---禮讓
讓出CPU執行其他線程

 1 package com.hanqi.maya.test;
 2 
 3 public class TestPriority {
 4     public static void main(String[] args) {
 5         MyThread11 mt1 = new MyThread11("超人");
 6         MyThread2 mt2 = new MyThread2("蝙蝠俠");
 7         mt1.setPriority(Thread.NORM_PRIORITY + 5);//設置優先級
 8         mt1.start();
 9         mt2.start();
10         System.out.println("123");
11         try {
12             Thread.sleep(2000);
13         } catch (InterruptedException e) {
14             // TODO Auto-generated catch block
15             e.printStackTrace();
16         }
17         System.out.println(456);
18     }
19 }
20 
21 
22 class MyThread11 extends Thread {
23     
24     public MyThread11(String name) {
25         super(name);//調用父類構造方法將name穿減去
26     }
27 
28     public void run() {
29         for (int i = 0; i < 50; i++) {
30             System.out.println(this.getName() + ": " + i);
31         }
32     }
33 }
34 
35 class MyThread2 extends Thread {
36     
37     public MyThread2(String name) {
38         super(name);
39     }
40     
41     public void run() {
42         for (int i = 0; i < 50; i++) {
43             System.out.println(this.getName() + ": " + i);
44         }
45     }
46 }

wait()

  • 導致當前線程等待,直到另一個線程調用此對象的notify()方法或notifyAll()方法,或指定的時間已過。

notify()

  • public final void notify()
    喚醒正在等待對象監視器的單個線程。 如果任何線程正在等待這個對象,其中一個被選擇被喚醒。 選擇是任意的,並且由實施的判斷發生。 線程通過調用wait方法之一等待對象的監視器。

    喚醒的線程將無法繼續,直到當前線程放棄此對象上的鎖定為止。 喚醒的線程將以通常的方式與任何其他線程競爭,這些線程可能正在積極地競爭在該對象上進行同步; 例如,喚醒的線程在下一個鎖定該對象的線程中沒有可靠的權限或缺點。


notifyAll()---

  • public final void notifyAll()
    喚醒正在等待對象監視器的所有線程。 線程通過調用wait方法之一等待對象的監視器。

    喚醒的線程將無法繼續,直到當前線程釋放該對象上的鎖。 喚醒的線程將以通常的方式與任何其他線程競爭,這些線程可能正在積極地競爭在該對象上進行同步; 例如,喚醒的線程在下一個鎖定該對象的線程中不會有可靠的特權或缺點。

4, 線程同步
synchronized

線程的同步是保證多線程安全訪問競爭資源的一種手段。

當多個線程同時讀寫同一份共享資源的時候,可能會引起沖突。這時候,我們需要引入線程“同步”機制,即各位線程之間要有個先來後到的執行。

線程同步的真實意思和字面意思恰好相反。線程同步的真實意思,其實是“排隊”:幾個線程之間要排隊,一個一個對共享資源進行操作,而不是同時進行操作。

比如銀行取錢:

銀行卡余額3000,A從取款機取2000,B也想從支付寶這張卡轉出2000,這時就要用到線程同步

線程同步,可以在方法聲明中用 synchronized 關鍵字

也可以用 synchronized 關鍵字將代碼塊包起來實現

 1 package com.hanqi.util;
 2 
 3 public class Bank {
 4     public int money=3000;
 5     
 6     public /*synchronized*/ void getMoney(String name,int mon){
 7         synchronized(this){
 8             if(money>mon&&money>0){
 9                 try {
10                     Thread.sleep(1);
11                 } catch (InterruptedException e) {
12                     e.printStackTrace();
13                 }
14                 
15                 money-=mon;
16                 System.out.println("hi,"+name);
17                 System.out.println("取款"+mon);
18                 System.out.println("余額:"+money);
19                 
20             }else{
21                 System.out.println("hi,"+name);
22                 System.out.println("取款失敗");
23                 System.out.println("余額:"+money);
24             }
25         }
26     }
27 }
 1 package com.hanqi.util;
 2 
 3 public class Threadt1 {
 4 
 5     public static void main(String[] args) {
 6         MyThread mt=new MyThread();
 7         
 8         Thread t1=new Thread(mt);
 9         Thread t2=new Thread(mt);
10         
11         t1.setName("Tom");
12         t2.setName("BomSa");
13         
14         t1.start();
15         t2.start();
16     }
17 }
18 class MyThread implements Runnable{
19     public Bank bank=new Bank();
20     @Override
21     public void run() {
22         bank.getMoney(Thread.currentThread().getName(), 2000);
23     }
24 }

五、死鎖概念

所謂死鎖,是兩個甚至多個線程被永久阻塞時的一種運行局面,這種局面的生成伴隨著至少兩個線程和兩個或者多個資源。兩個或兩個以上的進程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。由於資源占用是互斥的,當某個進程提出申請資源後,使得有關進程在無外力協助下,永遠分配不到必需的資源而無法繼續運行,這就產生了一種特殊現象死鎖。

 1 package com.hanqi.util;
 2 
 3 public class Deadlock implements Runnable {
 4 
 5     
 6     public static void main(String[] args) {
 7         
 8         Deadlock d1 = new Deadlock();
 9         Deadlock d2 = new Deadlock();
10         
11         d1.flag = 1;
12         d2.flag = 2;
13         
14         Thread t1 = new Thread(d1);
15         Thread t2 = new Thread(d2);
16         
17         t1.start();
18         t2.start();
19         
20     }
21     
22     public static Object obj1 = new Object();
23     public static Object obj2 = new Object();
24 
25     public int flag;
26 
27     @Override
28     public void run() {
29         if (flag == 1) {
30             System.out.println("這是第" + flag + "個線程");
31             synchronized (obj1) {
32                 try {
33                     Thread.sleep(500);
34                 } catch (InterruptedException e) {
35                     e.printStackTrace();
36                 }
37                 synchronized (obj2) {
38                     System.out.println("第" + flag + "個線程結束");
39                 }
40             }
41         }
42         
43         if (flag == 2) {
44             System.out.println("這是第" + flag + "個線程");
45             synchronized (obj2) {
46                 try {
47                     Thread.sleep(500);
48                 } catch (InterruptedException e) {
49                     e.printStackTrace();
50                 }
51                 synchronized (obj1) {
52                     System.out.println("第" + flag + "個線程結束");
53                 }
54             }
55         }
56 
57     }
58 
59 }

Java之線程,常用方法,線程同步,死鎖