第12篇-JAVA 多線程

第12篇-JAVA 多線程

  • 每篇一句 :不要只看到艱難,要看艱難後面的勝利

  • 初學心得: 敢於嘗試,就等於你已經向成功邁出了第一步

  • (筆者:JEEP/711)[JAVA筆記 | 時間:2017-04-20| JAVA 多線程 ]


1.進程與線程

1.什麼是進程

  • 程序是指令和數據的有序的集合,其本身沒有任何運行的含義,是一個靜態的概念

  • 進程是一個具有一定獨立功能的程序,一個實體

  • 幾乎所有的操作系統都支持同時運行多個任務,一個任務通常就是一個程序,每個運行中的程序就是一個進程

  • 當一個程序運行時,內部可能包含了多個順序執行流,每個順序執行流就是一個線程

2.進程的狀態: 進程執行時的間斷性,決定了進程可能具有多種狀態,事實上,運行中的進程具有

  • 以下三種基本狀態:

  • 1.就緒狀態(Ready)

  • 2.運行狀態(Running)

  • 3.阻塞狀態(Blocked)

3.線程

  • 線程實際上是進程基礎之上的進一步劃分,一個進程啓動之後,裏面的若干程序,又可以劃分成若干個線程

  • 線程:是進程中的一個執行路徑,共享一個內存空間,線程之間可以自由切換

  • 併發執行,一個進程最少有一個線程(單線程程序)

4.線程實現的兩種方式

  • 在java中如果想要實現多線程操作,兩種實現方法

  • 1.一種是繼承Thrcad類

  • 2.另一種是實現Runnable接口

5.多線程編程的優勢

  • 進程間不能共享內存,但線程之間共享內存非常容易

  • 系統創建進程需要爲該進程重新分配系統資源,但創建線程則代價小得多,因此使用多線程來實現多任務併發比多進程的效率高

  • Java語言內置的多線程功能支持,而不是單純地作爲底層操作系統的調度方式,從而簡化了Java的多線程編程

2.繼承Thread類創建線程類

(1)定義Thread類的子類,並重寫該類的run方法,該run方法的方法體就是代表了線程需要完成的任務 
因此,我們經常把run方法稱爲線程執行體 
(2)創建Thread子類的實例,即創建了線程對象 
(3)調用線程對象的start方法來啓動該線程 
(4)join線程:Thread提供了讓一個線程等待另一個線程完成的方法:join() 方法 
當在某個程序執行流中調用其他線程的join()方法時,調用線程將被阻塞,直到被join方法加入的join線程完成爲止 
join()方法通常由使用線程的程序調用,以將大問題劃分成許多小問題,每個小問題分配一個線程。當所有的小問題都得到處理後,再調用主線程來進一步操作

3.實現Runnable接口創建線程類

(1)定義Runnable接口的實現類,並重寫該接口的run方法,該run方法的方法體同樣是該線程的線程執行體 
(2)創建Runnable實現類的實例,並以此實例作爲Thread的target來創建Thread對象,該Thread對象纔是真正的線程對象 
(3)調用線程對象的start方法來啓動該線程

4.線程睡眠

如果我們需要讓當前正在執行的線程暫停一段時間,並進入阻塞狀態 
則可以通過調用Thread類的靜態sleep方法,sleep方法有兩種重載的形式:

  • static void sleep(long millis):讓當前正在執行的線程暫停millis毫秒,並進入阻塞狀態,該方法受到系統計時器和線程調度器的精度和準確度的影響

  • static void sleep(long millis, int nanos):讓當前正在執行的線程暫停millis毫秒加nanos毫微妙,並進入阻塞狀態,該方法受到系統計時器和線程調度器的精度和準確度的影響

5.兩種線程方式的對比

採用實現Runnable接口方式的多線程:

  • 線程類只是實現了Runnable接口,還可以可以繼承其他類

  • 在這種方式下,可以多個線程共享同一個target對象,所以非常適合多個相同線程來處理同一份資源的情況

  • 從而可以將CPU,代碼和數據分開,形成清晰的模型,較好地體現了面向對象的思想

  • 劣勢:編程稍稍複雜,如果需要訪問當前線程,必須使用Thread.currentThread()方法

採用繼承Thread類方式的多線程:

  • 劣勢:因爲線程類已經繼承了Thread類,所以不能再繼承其他父類

  • 優勢:編寫簡單,如果需要訪問當前線程,無需使用Thread.currentThread()方法,直接使用this即可獲得當前線程

6.線程生命週期

線程聲明週期:

  • 就緒狀態

  • 執行狀態

  • 阻塞狀態

7.線程安全問題

多條線程併發修改共享資源就容易引發線程安全問題 
使用同步解決多線程共享數據出現安全問題 
多個線程有可能同時處理同一個資源 
線程同步就是指多個線程在同一個時間段內只能有一個線程執行指定代碼 
其他線程等待此線程完成之後纔可以繼續執行 
同步代碼塊synchronized(要同步對象){要同步的操作} 
同步方法public synchronized void method(){要同步的操作} 
同步代碼會帶來性能降低的問題,提高數據的安全性,犧牲性能來保證安全 
同步代碼塊 
Java的多線程支持引入了同步監視器來解決這個問題,使用同步監視器的通用方法就是同步代碼塊 
synchronized後括號裏的obj就是同步監視器,上面代碼的含義是:線程開始執行同步代碼塊之前,必須先獲得對同步監視器的鎖定 
同步方法 
Java的多線程安全支持還提供了同步方法,同步方法就是使用synchronized關鍵字來修飾某個方法,則該方法成爲同步方法 
對於同步方法而言,無需顯式指定同步監視器,同步方法的同步監視器是this,也就是該對象本身 
線程死鎖 
過多的同步有可能死鎖,死鎖的操作一般是在程序運行的時候纔有可能出現 
多線程中要進行資源的共享,就需要同步,但同步過多,就可能造成死鎖

8.線程創建啓動實例

1.package cn.jeep;
2.//繼承Thread類
3.public class XianChengDemo extends Thread{
4.  private int i;//私有成員屬性
5.  //重寫run方法,run方法的方法體就是線程執行體
6.  public void run(){
7.      for(;i<100;i++){
8.          //當線程類繼承Thread類時,直接使用this.及格獲取當前線程
9.          //Thread對象的getNname()返回當前線程名字
10.          //因此可以直接調用getName方法返回當前線程名字
11.          System.out.println(getName()+" "+i);
12.      }
13.  }
14.  //主方法入口
15.  public static void main(String[] args) {
16.      for(int i=0;i<100;i++){
17.          //調用Thread的currenTread方法獲取當前線程
18.          System.out.println(Thread.currentThread().getName()+" "+i);
19.          if(i == 20){
20.              //創建並啓動第一個線程
21.              new XianChengDemo().start();
22.              //創建並啓動第二個線程
23.              new XianChengDemo().start();    
24.          }
25.      }
26.  }
27.}
1.package cn.runnable;
2.//聲明一個類
3.public  class RunnableDenmo implements Runnable{
4.  private int i;//私有屬性
5.  //重寫run方法
6.  public void run(){
7.      for(;i<100;i++){
8.          //當線程類實現Runnable接口時
9.          //如果想獲取當前線程,只能用Thread.currentTread()方法
10.          System.out.println(Thread.currentThread().getName()+" "+i);
11.      }
12.  }
13.  //主方法
14.  public static void main(String[] args) {
15.      for(int i=0;i<100;i++){
16.          System.out.println(Thread.currentThread().getName()+" "+i);
17.          if(i == 20){
18.              RunnableDenmo st = new RunnableDenmo();
19.              //通過new Thread(target,name)方法創建新線程
20.              new Thread(st,"新線程1").start();
21.              new Thread(st,"新線程2").start();
22.          }
23.      }
24.  }
25.}
1.package cn.callable;
2.import java.util.concurrent.Callable;
3.import java.util.concurrent.FutureTask;
4.public class CallableDemo {
5.  public static void main(String[] args) {
6.      //創建Callable對象
7.      @SuppressWarnings("unused")
8.      CallableDemo cl = new CallableDemo();
9.      //先使用Lambda表達式創建Callable<Integer>對象
10.      //使用FutureTask來包裝Callable對象
11.      FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{
12.          int i = 0;
13.          for(;i<100;i++){
14.              System.out.println(Thread.currentThread().getName()+"的循環變量的i的值:"+i);
15.          }
16.          //call()方法可以有返回值
17.          return i;
18.      });
19.      for(int i=0;i<100;i++){
20.          if(i==20){
21.              //實質還是以Callable對象來創建並啓動線程
22.              new Thread(task,"有返回值得線程").start();
23.          }
24.      }
25.      try{
26.      //獲取線程返回值
27.      System.out.println("子線程的返回值:"+task.get());
28.          }catch(Exception ex){
29.      ex.printStackTrace();
30.      }
31.  }
32.}

第12篇-JAVA 多線程

  • 每篇一句 :不要只看到艱難,要看艱難後面的勝利

  • 初學心得: 敢於嘗試,就等於你已經向成功邁出了第一步

  • (筆者:JEEP/711)[JAVA筆記 | 時間:2017-04-20| JAVA 多線程 ]

目錄導航




1.進程與線程

1.什麼是進程

  • 程序是指令和數據的有序的集合,其本身沒有任何運行的含義,是一個靜態的概念

  • 進程是一個具有一定獨立功能的程序,一個實體

  • 幾乎所有的操作系統都支持同時運行多個任務,一個任務通常就是一個程序,每個運行中的程序就是一個進程

  • 當一個程序運行時,內部可能包含了多個順序執行流,每個順序執行流就是一個線程

2.進程的狀態: 進程執行時的間斷性,決定了進程可能具有多種狀態,事實上,運行中的進程具有

  • 以下三種基本狀態:

  • 1.就緒狀態(Ready)

  • 2.運行狀態(Running)

  • 3.阻塞狀態(Blocked)

3.線程

  • 線程實際上是進程基礎之上的進一步劃分,一個進程啓動之後,裏面的若干程序,又可以劃分成若干個線程

  • 線程:是進程中的一個執行路徑,共享一個內存空間,線程之間可以自由切換

  • 併發執行,一個進程最少有一個線程(單線程程序)

4.線程實現的兩種方式

  • 在java中如果想要實現多線程操作,兩種實現方法

  • 1.一種是繼承Thrcad類

  • 2.另一種是實現Runnable接口

5.多線程編程的優勢

  • 進程間不能共享內存,但線程之間共享內存非常容易

  • 系統創建進程需要爲該進程重新分配系統資源,但創建線程則代價小得多,因此使用多線程來實現多任務併發比多進程的效率高

  • Java語言內置的多線程功能支持,而不是單純地作爲底層操作系統的調度方式,從而簡化了Java的多線程編程

2.繼承Thread類創建線程類

(1)定義Thread類的子類,並重寫該類的run方法,該run方法的方法體就是代表了線程需要完成的任務 
因此,我們經常把run方法稱爲線程執行體 
(2)創建Thread子類的實例,即創建了線程對象 
(3)調用線程對象的start方法來啓動該線程 
(4)join線程:Thread提供了讓一個線程等待另一個線程完成的方法:join() 方法 
當在某個程序執行流中調用其他線程的join()方法時,調用線程將被阻塞,直到被join方法加入的join線程完成爲止 
join()方法通常由使用線程的程序調用,以將大問題劃分成許多小問題,每個小問題分配一個線程。當所有的小問題都得到處理後,再調用主線程來進一步操作

3.實現Runnable接口創建線程類

(1)定義Runnable接口的實現類,並重寫該接口的run方法,該run方法的方法體同樣是該線程的線程執行體 
(2)創建Runnable實現類的實例,並以此實例作爲Thread的target來創建Thread對象,該Thread對象纔是真正的線程對象 
(3)調用線程對象的start方法來啓動該線程

4.線程睡眠

如果我們需要讓當前正在執行的線程暫停一段時間,並進入阻塞狀態 
則可以通過調用Thread類的靜態sleep方法,sleep方法有兩種重載的形式:

  • static void sleep(long millis):讓當前正在執行的線程暫停millis毫秒,並進入阻塞狀態,該方法受到系統計時器和線程調度器的精度和準確度的影響

  • static void sleep(long millis, int nanos):讓當前正在執行的線程暫停millis毫秒加nanos毫微妙,並進入阻塞狀態,該方法受到系統計時器和線程調度器的精度和準確度的影響

5.兩種線程方式的對比

採用實現Runnable接口方式的多線程:

  • 線程類只是實現了Runnable接口,還可以可以繼承其他類

  • 在這種方式下,可以多個線程共享同一個target對象,所以非常適合多個相同線程來處理同一份資源的情況

  • 從而可以將CPU,代碼和數據分開,形成清晰的模型,較好地體現了面向對象的思想

  • 劣勢:編程稍稍複雜,如果需要訪問當前線程,必須使用Thread.currentThread()方法

採用繼承Thread類方式的多線程:

  • 劣勢:因爲線程類已經繼承了Thread類,所以不能再繼承其他父類

  • 優勢:編寫簡單,如果需要訪問當前線程,無需使用Thread.currentThread()方法,直接使用this即可獲得當前線程

6.線程生命週期

線程聲明週期:

  • 就緒狀態

  • 執行狀態

  • 阻塞狀態

7.線程安全問題

多條線程併發修改共享資源就容易引發線程安全問題 
使用同步解決多線程共享數據出現安全問題 
多個線程有可能同時處理同一個資源 
線程同步就是指多個線程在同一個時間段內只能有一個線程執行指定代碼 
其他線程等待此線程完成之後纔可以繼續執行 
同步代碼塊synchronized(要同步對象){要同步的操作} 
同步方法public synchronized void method(){要同步的操作} 
同步代碼會帶來性能降低的問題,提高數據的安全性,犧牲性能來保證安全 
同步代碼塊 
Java的多線程支持引入了同步監視器來解決這個問題,使用同步監視器的通用方法就是同步代碼塊 
synchronized後括號裏的obj就是同步監視器,上面代碼的含義是:線程開始執行同步代碼塊之前,必須先獲得對同步監視器的鎖定 
同步方法 
Java的多線程安全支持還提供了同步方法,同步方法就是使用synchronized關鍵字來修飾某個方法,則該方法成爲同步方法 
對於同步方法而言,無需顯式指定同步監視器,同步方法的同步監視器是this,也就是該對象本身 
線程死鎖 
過多的同步有可能死鎖,死鎖的操作一般是在程序運行的時候纔有可能出現 
多線程中要進行資源的共享,就需要同步,但同步過多,就可能造成死鎖

8.線程創建啓動實例

1.package cn.jeep;
2.//繼承Thread類
3.public class XianChengDemo extends Thread{
4.  private int i;//私有成員屬性
5.  //重寫run方法,run方法的方法體就是線程執行體
6.  public void run(){
7.      for(;i<100;i++){
8.          //當線程類繼承Thread類時,直接使用this.及格獲取當前線程
9.          //Thread對象的getNname()返回當前線程名字
10.          //因此可以直接調用getName方法返回當前線程名字
11.          System.out.println(getName()+" "+i);
12.      }
13.  }
14.  //主方法入口
15.  public static void main(String[] args) {
16.      for(int i=0;i<100;i++){
17.          //調用Thread的currenTread方法獲取當前線程
18.          System.out.println(Thread.currentThread().getName()+" "+i);
19.          if(i == 20){
20.              //創建並啓動第一個線程
21.              new XianChengDemo().start();
22.              //創建並啓動第二個線程
23.              new XianChengDemo().start();    
24.          }
25.      }
26.  }
27.}
1.package cn.runnable;
2.//聲明一個類
3.public  class RunnableDenmo implements Runnable{
4.  private int i;//私有屬性
5.  //重寫run方法
6.  public void run(){
7.      for(;i<100;i++){
8.          //當線程類實現Runnable接口時
9.          //如果想獲取當前線程,只能用Thread.currentTread()方法
10.          System.out.println(Thread.currentThread().getName()+" "+i);
11.      }
12.  }
13.  //主方法
14.  public static void main(String[] args) {
15.      for(int i=0;i<100;i++){
16.          System.out.println(Thread.currentThread().getName()+" "+i);
17.          if(i == 20){
18.              RunnableDenmo st = new RunnableDenmo();
19.              //通過new Thread(target,name)方法創建新線程
20.              new Thread(st,"新線程1").start();
21.              new Thread(st,"新線程2").start();
22.          }
23.      }
24.  }
25.}
1.package cn.callable;
2.import java.util.concurrent.Callable;
3.import java.util.concurrent.FutureTask;
4.public class CallableDemo {
5.  public static void main(String[] args) {
6.      //創建Callable對象
7.      @SuppressWarnings("unused")
8.      CallableDemo cl = new CallableDemo();
9.      //先使用Lambda表達式創建Callable<Integer>對象
10.      //使用FutureTask來包裝Callable對象
11.      FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{
12.          int i = 0;
13.          for(;i<100;i++){
14.              System.out.println(Thread.currentThread().getName()+"的循環變量的i的值:"+i);
15.          }
16.          //call()方法可以有返回值
17.          return i;
18.      });
19.      for(int i=0;i<100;i++){
20.          if(i==20){
21.              //實質還是以Callable對象來創建並啓動線程
22.              new Thread(task,"有返回值得線程").start();
23.          }
24.      }
25.      try{
26.      //獲取線程返回值
27.      System.out.println("子線程的返回值:"+task.get());
28.          }catch(Exception ex){
29.      ex.printStackTrace();
30.      }
31.  }
32.}

9.線程同步實例

1.package cn.tongbu;
2.public class TB {
3.  public static void main(String[] args) {
4.      TbDemo tb = new TbDemo(); 
5.      Thread t1 = new Thread(tb,"小明");
6.      Thread t2 = new Thread(tb,"小白");
7.      Thread t3 = new Thread(tb,"小紅");
8.      Thread t4 = new Thread(tb,"小黑");
9.      Thread t5 = new Thread(tb,"小華");
10.      t1.start();
11.      t2.start();
12.      t3.start();
13.      t4.start();
14.      t5.start();
15.  }
16.}
17.class TbDemo implements Runnable{
18.  Object obj = new Object();//同步得標記
19.  @Override
20.  public void run() {
21.      //線程同步代碼塊
22.      //synchronized(obj){
23.      //}
24.      say();//調用同步方法
25.  }
26.  /**
27.   * 同步方法
28.   */
29.  public synchronized void say(){
30.      System.out.println(Thread.currentThread().getName()+"正在通話中....");
31.      try {
32.          Thread.sleep(2000);//2秒
33.      } catch (InterruptedException e) {
34.          e.printStackTrace();
35.      }
36.      System.out.println(Thread.currentThread().getName()+"通話結束!");
37.  }
38.}

10.線程死鎖實例

1.package cn.sisuo;
2.public class SiSuoDemo {
3.  public static void main(String[] args) {
4.      new Ss();
5.  }
6.}
7.//警
8.class XingJing{
9.  //同步方法
10.  public synchronized void say(FeiTu f){
11.      System.out.println("警方說先放人質,在實行抓捕");
12.      f.dos();
13.  }
14.  public synchronized void dos(){
15.      System.out.println("同意匪徒要求,在實行抓捕");
16.  }
17.}
18.//匪徒
19.class FeiTu{
20.  //同步方法
21.  public synchronized void say(XingJing x){
22.      System.out.println("匪徒說先找一輛車,在放人質");
23.      x.dos();
24.  }
25.  public synchronized void dos(){
26.      System.out.println("先找一輛車,再放人質");
27.  }
28.}
29./**
30. * 死鎖線程
31. * @author JEEP-711
32. *
33. */
34.class Ss implements Runnable{
35.  XingJing xj = new XingJing();
36.  FeiTu ft = new FeiTu();
37.  //模擬線程死鎖構造方法
38.  public Ss(){
39.      new Thread(this).start();//爲了快速實現
40.      ft.say(xj);
41.  }
42.  @Override
43.  public void run() {
44.      xj.say(ft);
45.  }
46.}

11.多線程同步銀行取錢實例

1.package cn.chinabank;//中國銀行包
2.//聲明一個賬戶類
3.public class Account {
4.  private String name;//私有屬性-姓名
5.  private double number=0.0;//私有屬性-餘額
6.  //構造方法並傳值
7.  public Account(String name,double number){
8.      this.name = name;//當前姓名
9.      this.number = number;//當前金額
10.  }
11.  //取得getName方法
12.  public String getName() {
13.      return name;
14.  }
15.  //設置setName方法
16.  public void setName(String name) {
17.      this.name = name;
18.  }
19.  //取得getNumber方法
20.  public double getNumber() {
21.      return number;
22.  }
23.  //設置setNumber方法
24.  public void setNumber(double number) {
25.      this.number = number;
26.  }
27.}
1.package cn.chinabank;//中國銀行包
2.import java.util.Scanner;
3.//聲明一箇中國銀行類
4.public class ChinaBank {
5.  //定義主方法
6.  public static void main(String[] args) {
7.      @SuppressWarnings("resource")
8.      Scanner sc = new Scanner(System.in);//接收控制檯鍵盤輸入
9.      System.out.println("----------------------");
10.      System.out.println("----歡迎您進入中國銀行-----");
11.      System.out.println("---請選擇您需要辦理的業務---");
12.      System.out.println("1.存款"+"\t"+" 2.取款"+"\t"+"3.退出");
13.      System.out.println("----------------------");
14.      int sr = sc.nextInt();//接收一個數
15.      //接收用戶輸入
16.      switch(sr){
17.      //輸入1則進入存款功能
18.          case 1:
19.               System.out.println("請您輸入存款金額:");//提示用戶存款金額
20.              double  number = sc.nextDouble();//接收用戶輸入金額
21.              @SuppressWarnings("unused")
22.              Account account1 = new Account("asdd",number);//創建Accound對象
23.              System.out.println("請您將鈔票疊整齊後放入存鈔口..");//提示用戶將鈔票放入存鈔口
24.              try{
25.                  Thread.sleep(2000);//模仿現實銀行存鈔,設置睡眠時間兩秒
26.                  System.out.println("正在存鈔,請稍後...");//將輸出等待
27.                  Thread.sleep(4000);//將其過程等待四秒
28.                  System.out.println("存款成功!");//則輸出存款成功
29.          System.out.println("您的存款金額爲:"+number+"\t"+"當前賬戶餘額爲:"+number);
30.                                                              //輸出存款金額
31.          System.out.println("1.是否打印憑條"+"\t"+"2.是否繼續存款"+"\t"+"3.退出");
32.                                                            //用戶繼續選擇
33.                  int sr1 = sc.nextInt();//接收用戶輸入
34.                  switch(sr1){
35.                  //接收用戶是否打印憑條
36.                      case 1:
37.                          System.out.println("dce");
38.                          System.out.println("打印完成,退出打印");
39.                          break;
40.                      //接收用戶是否繼續存款
41.                      case 2:
42.                          System.out.println("繼續存款");
43.                          banking();
44.                          break;
45.                      //接收用戶退出
46.                      case 3:
47.                          System.out.println("您已退出存款選項");
48.                          break;
49.                      //防止隨意輸入    
50.                      default :
51.                          System.out.println("您輸入有誤,請重新輸入");
52.                          break;
53.                  }
54.              }catch(Exception e){
55.                  e.printStackTrace();
56.              }
57.              break;
58.          //輸入2則進入取款功能    
59.          case 2:System.out.println("請輸出您的取款金額:");
60.              Account account2 = new Account("小明",7000);//創建Account對象並傳參數
61.              DrawMoney ka = new DrawMoney(account2,3000);//創建銀行卡取款3000元
62.              DrawMoney zhe = new DrawMoney(account2,4000);//創建存摺取款2000元
63.              new Thread(ka).start();//開啓銀行卡線程
64.              new Thread(zhe).start();//開啓存摺線程
65.              break;
66.          //輸入3則退出銀行系統
67.          case 3:
68.              System.out.println("您已安全退出中國銀行系統,感謝您的光臨!");
69.              System.exit(0);//退出指令
70.              break;
71.          //防止用戶隨意輸入則進行友情提示
72.          default :
73.              System.out.println("您輸入有誤,請重新輸入");
74.              break;
75.      }
76.  }
77.  //繼承存款金額
78.  public static void banking(){
79.      @SuppressWarnings("resource")
80.      Scanner sc2 = new Scanner(System.in);//接收控制檯鍵盤輸入
81.               System.out.println("請您輸入存款金額:");//提示用戶存款金額
82.              double  number = sc2.nextDouble();//接收用戶輸入金額
83.              @SuppressWarnings("unused")
84.              Account account2 = new Account("asdd",number);//創建Accound對象
85.              System.out.println("請您將鈔票疊整齊後放入存鈔口..");//提示用戶將鈔票放入存鈔口
86.              try{
87.                  Thread.sleep(2000);//模仿現實銀行存鈔,設置睡眠時間兩秒
88.                  System.out.println("正在存鈔,請稍後...");//將輸出等待
89.                  Thread.sleep(4000);//將其過程等待四秒
90.                  System.out.println("存款成功!");//則輸出存款成功
91.          System.out.println("您的存款金額爲:"+number+"\t"+"當前賬戶餘額爲:"+number);
92.                                                       //輸出存款金額
93.                  System.out.println("1.是否打印憑條"+"\t"+"\t"+"2.退出");//用戶繼續選擇
94.                  int sr3 = sc2.nextInt();//接收用戶輸入
95.                  switch(sr3){
96.                  //接收用戶是否打印憑條
97.                      case 1:
98.                          System.out.println("dce");
99.                          System.out.println("打印完成,退出打印");
100.                          break;
101.                      case 2:
102.                          System.out.println("您已退出存款選項");
103.                          break;
104.                      //防止隨意輸入    
105.                      default :
106.                          System.out.println("您輸入有誤,請重新輸入");
107.                          break;
108.                  }
109.              }catch(Exception e){
110.                  e.printStackTrace();
111.              }   
112.  }
113.}
1.package cn.chinabank;//中國銀行包
2.//取錢類實現Runnable接口
3.public class DrawMoney implements Runnable{
4.  private Account account;//私有屬性-賬戶
5.  private double money;//私有屬性-金額
6.  //構造方法並傳值
7.  public DrawMoney(Account account,double money){
8.      this.account = account;//當前賬戶
9.      this.money = money;//當前金額
10.  }
11.  @Override
12.  public void run() {
13.      while(true){
14.          //線程同步塊
15.          //相當於將account隊形鎖住,只有執行完代碼塊,纔可以釋放,其他線程才能拿到
16.          //account必須是同一個
17.          synchronized(account){
18.              //如果賬戶金額大於等於金額
19.              if(account.getNumber()>=money){
20.                  account.setNumber(account.getNumber()-money);//賬戶餘額減去取款金額
21.                  //輸出取款成功,並顯示當前賬戶剩餘金額
22.                  System.out.println("你已取款成功,您取款金額爲:"+money+"元,剩餘金額爲:"+account.getNumber()+"元");
23.                  //異常處理
24.                  try{
25.                  Thread.sleep(2000);//調用方法設置睡眠兩秒
26.                  }catch(InterruptedException e){
27.                      e.printStackTrace();
28.              }
29.              //否則提示取款不成功,顯示當前賬戶餘額
30.              }else{
31.                  System.out.println("對不起,您的賬戶餘額不足,您當前賬戶餘額剩餘"+account.getNumber()+"元");//輸出賬戶金額
32.              }
33.          }
34.          break;
35.      }   
36.  }

12.多線程 - 生產者與消費者應用案例

多線程的開發中有一個最經典的操作案例,就是生產者-消費者 
生產者不斷生產產品,消費者不斷取走產品

1./**
2.* 生產者與消費者應用案例
3.* sleep與wait區別
4.* sleep讓當前的線程進入休眠狀態,讓出cpu,讓其他線程執行
5.* 如果用同步的話,有對象鎖的時候,是不會釋放的,只能等待此線程使用完,纔可以使用
6.* wait會釋放對象鎖,必須等待其他線程喚醒
7.* @author JEEP-711
8.*
9.*/
10.public class ScXf {
11.  public static void main(String[] args) {
12.      Phones p = new Phones(null, null);//創建Phones對象
13.      PhoneSc s = new PhoneSc(p);//創建PhoneSc對象 
14.      PhoneXf x = new PhoneXf(p);//創建PhoneXf對象
15.      new Thread(s).start();//啓動生產者線程
16.      new Thread(x).start();//啓動消費者線程
17.  }
18.}
19./**
20. * 手機生產者,單獨的生產者,實現Runnable接口
21. * @author JEEP-711
22. *
23. */
24.class PhoneSc implements Runnable{
25.  private Phones phones;
26.  public PhoneSc(Phones phones){
27.      this.phones = phones;
28.  }
29.  @Override
30.  public void run() {
31.      //不斷地生產20份,生產的過程
32.      for (int i = 0; i < 50; i++) {
33.          if(i%2==0){
34.              phones.set("金立手機", "金立手機,中國造!");
35.          }else{
36.              phones.set("小米手機", "小米手機,爲發燒而生!");
37.          }
38.      }
39.  }
40.}
41./**
42.* 手機消費者,顧客
43. * @author JEEP-711
44. *
45. */
46.class PhoneXf implements Runnable{
47.  private Phones phones;
48.  public PhoneXf(Phones phones){
49.      this.phones = phones;
50.  }
51.  @Override
52.  public void run() {
53.      for (int i = 0; i < 50; i++) {
54.          phones.get();//調用消費產品方法
55.      }
56.  }
57.}
58./**
59.* 產品的對象,生產的手機
60.* @author JEEP-711
61.*
62. */
63.class Phones{
64.  @Override
65.  public String toString() {
66.      return "Phones [name=" + name + ", content=" + content + "]";
67.  }
68.  private String name;
69.  private String content;
70.  /**true表示可以生產,false表示可以消費
71.   * 作爲標記,如何flag等於true表示可以生產,如何flag等於false表示不可生產
72.   * 如果flag等於false表示可以消費狀態,可以取走,flag等於true表示不能取走
73.   * 解決重複值得問題
74.   */
75.  private boolean flag = true;//表示可以生產,false表示可以消費
76.  //構造方法
77.  public Phones(String name, String content) {
78.      super();
79.      this.name = name;
80.      this.content = content;
81.  }
82.  //取得名稱方法
83.  public String getName() {
84.      return name;
85.  }
86.  //設置名稱方法
87.  public void setName(String name) {
88.      this.name = name;
89.  }
90.  //取得內容方法
91.  public String getContent() {
92.      return content;
93.  }
94.  //設置內容方法
95.  public void setContent(String content) {
96.      this.content = content;
97.  }
98.  /**
99.   * 通過同步,解決了取值錯誤問題
100.   * @param name
101.   * @param content
102.   */
103.  //生產製造同步方法
104.  public synchronized void set(String name, String content){
105.      if(!flag){
106.          try {
107.          //調用該方法,當前線程進入等待池等待狀態,沒有指定時間,
108.          //需要其他線程喚醒,釋放對象鎖,讓出cpu
109.              this.wait();
110.          } catch (InterruptedException e) {
111.              e.printStackTrace();
112.          }
113.      }
114.      this.setName(name);
115.      try {
116.          Thread.sleep(300);
117.      } catch (InterruptedException e) {
118.          e.printStackTrace();
119.      }
120.      this.setContent(content);
121.      flag = false;//表示可以消費,取走
122.      this.notify();//喚醒在該監視器上的一個線程
123.  }
124.  //消費產品同步取值方法
125.  public synchronized void get(){
126.      if(flag){
127.          try {
128.              //調用該方法,當前線程進入等待池等待狀態,沒有指定時間,
129.              //需要其他線程喚醒,釋放對象鎖,讓出cpu
130.              this.wait();
131.          } catch (InterruptedException e) {
132.              e.printStackTrace();
133.          }
134.      }
135.      try {
136.          Thread.sleep(300);
137.      } catch (InterruptedException e) {
138.          e.printStackTrace();
139.      }
140.      System.out.println(this.getName()+":"+this.getContent());
141.      flag = true;
142.      this.notify();
143.  }
144.}

13.線程池

線程池是預先創建線程的一種技術,線程池在還沒有任務到來之前,創建一定數量的線程,放入空閒隊列中,然後對這些資源進行復用,減少頻繁的創建和銷燬對象 
java裏面線程池的頂級接口是Executor,是一個執行線程的工具 
線程池接口是ExecutorServise 
1.java.util.concurrent包:併發編程中很常用的實用工具包 
2.Executor接口:執行已提交Runnable任務的對象

ExecutorService接口: 
Executor提供了管理終止的方法,以及可能爲跟蹤一個或 
多個異步任務執行狀況而發生的Future的方法 
Executors類:此包中所定義的Executor、ExecutorService等的工廠和實現方法 
在Executors類裏提供了一些靜態工廠,生成一些常用的線程池 
newSingleThreadExecutor: 
創建一個單線程的線程池,這個線程池只有一個線程在工作,也就是相當於單線程 
串行執行所有的任務,如果這個唯一的線程因爲異常結束,那麼會有一個新的線程來替代它,此線程池保證所有任務的執行順序按照任務的提交順序執行 
newFixedThreadPool: 
創建固定大小的線程池,每次提交一個任務就創建一個線程,直到線程達到線程池的最大線程池的大小一旦達到最大值就會保持不變,如果某個線程因爲執行異常而結束,那麼線程池會補充一個新線程 
newCacheadThreadPool: 
創建一個可緩存的線程池,如果線程池的大小超過了處理任務所需要的線程,那麼就會回收部分空閒(60秒不執行)的線程,當任務數增加時,此線程池又可以智能的添加新線程來處理任務,此線程池不會對線程池大小做限制,線程池大小完全依賴於操作系統(或者說jvm)能夠創建的最大線程大小 
newSchediledThreadPool: 
創建一個大小無限制的線程池,此線程池支持定時以及週期性執行任務需求

使用線程池的步驟:

  • (1)調用Executors類的靜態工廠方法創建一個ExecutorService對象或ScheduledExecutorService對象,其中前者代表簡單的線程池,後者代表能以任務調度方式執行線程的線程池

  • (2)創建Runnable實現類或Callable實現類的實例,作爲線程執行任務

  • (3)調用ExecutorService對象的submit方法來提交Runnable實例或Callable實例;或調用ScheduledExecutorService的schedule來執行線程

  • (4)當不想提交任何任務時調用ExecutorService對象的shutdown方法來關閉線程池


初學(JAVA 多線程 高級階段) 難點: ★★★★★

希望每一篇文章都能夠對讀者們提供幫助與提升,這乃是每一位筆者的初衷


感謝您的閱讀 歡迎您的留言與建議


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章