Java之執行緒,常用方法,執行緒同步,死鎖
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 }