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.langClass 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之線程,常用方法,線程同步,死鎖