麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 開發設計 > 正文

黑馬程序員——【Java基礎】——多線程

2019-11-14 21:15:26
字體:
來源:轉載
供稿:網友
黑馬程序員——【java基礎】——多線程

---------- android培訓、java培訓、期待與您交流! ----------

一、概述

 ?。ㄒ唬┻M程

  正在執行中的程序,每一個進程執行都有一個執行順序。該順序是一個執行路徑,或者叫一個控制單元。

  (二)線程

  進程中的一個獨立的控制單元。線程在控制著進程的執行。一個進程中至少有一個線程。只要進程中有一個線程在執行,進程就不會結束。

  (三)多線程

  在java虛擬機啟動的時候會有一個java.exe的執行程序,也就是一個進程。該進程中至少有一個線程負責java程序的執行。而且這個線程運行的代碼存在于main方法中。該線程稱之為主線程。JVM啟動除了執行一個主線程,還有負責垃圾回收機制的線程。這種在一個進程中有多個線程執行的方式,就叫做多線程。

 ?。ㄋ模┒嗑€程創建線程的目的和意義

  1、創建目的:開啟一條執行路徑;運行的代碼就是執行路徑的任務。JVM創建的主線程的任務都定義在了主函數中。run方法中,定義的代碼就是線程要運行的任務代碼。

  2、創建的意義:可以讓程序產生同時運行效果;可以提高程序執行效率。

二、創建線程的方式

  創建線程共有兩種方式:繼承方式和實現方式。

 ?。ㄒ唬?ldquo;繼承Thread類”創建多線程

  Java API文檔的java.lang包中提供了Thread類,通過繼承Thread類,然后覆寫其run方法來創建線程。

  1、創建步驟

  (1)定義類,繼承Thread。

  (2)覆寫Thread類中的run方法。

 ?。?)調用start方法,啟動線程,調用run方法執行線程任務代碼。

  2、多線程run和start的特點

  為什么要覆蓋run方法呢?Thread類用于描述線程,該類定義了一個功能,用于存儲要運行的代碼,這些運行的代碼就存儲在run方法中。

  3、練習:創建兩線程,和主線程交替運行。示例代碼如下:

 1 //創建線程Test   2 class Test extends Thread{ 3     Test(String name){ 4          super(name); 5     } 6     //覆寫run方法 7     public void run(){ 8         for(int x=0;x<150;x++) 9        System.out.

  說明:上述代碼每次運行的結果都不一樣。因為多個線程都在獲取CPU的執行權,CPU執行到誰誰就運行。更明確一點說,在某一時刻,只能有一個程序在運行(多核除外)。CPU是在做著快速的切換,已達到看上去是同時在運行的效果。

  (二)“實現Runnable接口”創建多線程

  使用Thread類繼承方式有一個弊端,即如果該類本來就繼承了其他父類,那么就無法通過繼承Thread類來創建多線程。這樣就有了第二種創建線程的方式:實現Runnable接口,并覆寫其中的run方法來創建多線程。

  1、創建步驟

 ?。?)定義類實現Runnable接口。

 ?。?)覆蓋Runnable接口中的run方法。

 ?。?)通過Thread類創建線程對象。

 ?。?)將Runnable接口的子類對象作為實參傳遞給Thread類的構造方法。

  (5)調用Thread類中start方法啟動線程,start方法會自動調用Runnable接口子類的run方法。

  2、細節問題

  (1)為什么要將Runnable接口的子類對象傳遞給Thread類的構造函數?

  因為自定義的run方法所屬的對象是Runnable接口的子類對象,所以要讓線程去指定對象的run方法,就必須明確該run方法所屬對象。

 ?。?)實現Runnable的好處:將線程任務從線程的子類中分離出來,進行單獨的封裝。避免了Java單繼承的局限性。在定義線程時,建議使用實現方式。

  (三)“實現方式”和“繼承方式”的區別

  1、繼承方式:線程代碼存放在Thread子類的run方法中。

  2、實現方式:線程代碼存在接口的子類run方法中。

  (四)線程運行狀態結構圖

  運行狀態結構圖說明:

  1、被創建:等待啟動,調用start啟動。

  2、運行狀態:具有執行資格和執行權。

  3、臨時狀態(阻塞):有執行資格,但是沒有執行權。

  4、凍結狀態:遇到sleep(time)方法和wait()方法時,失去執行資格和執行權,sleep方法時間到或者調用notify()方法時,獲得執行資格,變為臨時狀態。

  5、消忙狀態:stop()方法,或者run方法結束。

  (五)獲取線程對象以及名稱

  1、原來的線程都有自己默認的名稱,命名方式是Thread-編號,編號從0開始。

  2、通過對象this.getName()、super(name),可以獲取線程對象的名稱。也可以通過Thread.currentThread().getName()標準方式獲取線程的名稱。說明:① static Thread currentThread():獲取當前線程對象。② getName():獲取線程名稱。

  3、設置線程名稱:setName(String name)方法或者構造函數都可以實現。

  4、局部變量在每一個線程區域中都有獨立的一份。

 ?。┚毩暎菏燮崩?/strong>

 1 /* 簡單賣票程序:多窗口同時賣票 */ 2 class Ticket implements Runnable//extends Thread{ 3     private  int tick = 100; 4     public void run(){ 5         while(true){ 6             if(tick>0) 7                 System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--); 8         } 9     }10 }11 class TicketDemo{12     public static void main(String[] args){13         Ticket t = new Ticket();//創建Runnable接口子類的實例對象14         //有多個窗口在同時賣票,創建三個線程15         Thread t1 = new Thread(t);// 16         Thread t2 = new Thread(t);17         Thread t3 = new Thread(t);18         //啟動線程19         t1.start();20         t2.start();21         t3.start();22     }23 }

三、多線程的安全問題

 ?。ㄒ唬┒鄠€線程“安全問題”

  1、問題的原因:當多條線程在操作同一個共享數據時,因為CPU的權限分配而出現一個線程對多條語句只執行一部分,還沒有執行完,另外一條線程參與進來執行,導致共享數據的錯誤。

  2、解決思路:對操作共享數據的多條語句,只能讓一個線程都執行完,而其他線程不能執行。

  (二)解決方式

  java對于多線程的安全問題提供了專業的解決方式:為需要操作共享數據的多條語句添加synchronized(同步)修飾符。有兩種解決方式:一種是同步代碼塊,另一種是同步函數。都是利用關鍵字synchronized來實現。

  1、同步代碼塊

  (1)格式:synchronized(對象){ 需要被同步的代碼塊 }

  同步可以解決安全問題的根本原因就在那個對象上。對象如同一把鎖,持有鎖的線程可以在執行同步代碼塊,而沒有持有鎖的線程即使獲取CPU執行權,也因為沒有獲取對象而無法執行同步代碼塊。

 ?。?)同步代碼塊使用的鎖是哪一個呢?任意對象。

  (3)同步代碼塊示例:為多線程賣票程序加上同步代碼塊,代碼如下:

 1 class Ticket implements Runnable { 2     private int tick = 100; 3     Object obj = new Object(); 4     public void run() { 5         while (true) { 6             // 給程序加同步,即鎖 7             synchronized (obj) { 8                 if (tick > 0) { 9                     try {// 使用線程中的sleep方法,模擬線程出現的安全問題10                     // 因為sleep方法有異常聲明,所以這里要對其進行處理11                         Thread.sleep(10);12                     } catch (Exception e) {13                     }14                     System.out.println(Thread.currentThread().getName()+ "..tick..=" + tick--);// 顯示線程名及余票數15                 }16             }17         }18     }19 }

  2、同步函數

  (1)格式:synchronized作為函數的修飾符,添加在函數上,就構成了同步函數。

  (2)同步函數用的是哪一個鎖呢?因為函數需要被對象調用,那么函數都持有一個所屬對象引用,即this。所以同步函數使用的鎖是this。

 ?。?)同步函數示例

1 public synchronized void add(int num){2     sun = sum + num;3     try{4         Thread.sleep(10);5         }catch(InterruptedException e){6         }7         System.out.println(“sum = ”+ sum);8 }

  3、靜態同步函數

 ?。?)格式:同步函數被static修飾后,就構成了靜態同步函數。

  (2)靜態同步函數使用的鎖是什么呢?Class對象。

  說明:因為靜態在進內存時,內存中沒有本類對象,但一定有該類對應的字節碼文件對象,因此,靜態同步函數使用的鎖是該函數所屬字節碼文件對象,即 類名.class,該對象的類型是Class,可以用getClass()方法獲取,也可以用 類名.class獲取。

  (3)靜態同步函數代碼示例:

 1 class StaticSynchDemo implements Runnable{ 2     private static int num = 100; 3     public void run(){ System.out.println(“num = ”+ num); }; 4     public static synchronized void show()    { 5         if(num>0) { 6             try{ 7                 Thread.sleep(10); 8                }catch(InterruptedException e){ } 9             System.out.println(Thread.currentThread().getName()+".....function...."+num--);10         }11     }12 }

 ?。ㄈ┩降那疤岷屠?/strong>

  1、同步的前提

 ?。?)必須要有兩個或者兩個以上的線程。

 ?。?)必須是多個線程使用同一個鎖。

 ?。?)必須保證同步中,只能有一個線程在運行

  2、同步的利弊

  利:解決了多線的安全問題。

  弊:多個線程需要判斷鎖,較為消耗資源。

 ?。ㄋ模┤绾螌ふ叶嗑€程中的安全問題

 ?。?)明確哪些代碼是多線程運行代碼。

 ?。?)明確共享數據。

 ?。?)明確多線程運行代碼中,哪些語句是操作共享數據的。

四、死鎖

 ?。ㄒ唬┧梨i:常見情景之一是“同步的嵌套”。

 ?。ǘ?ldquo;同步嵌套”出現的死鎖示例:

 1 /* 寫一個死鎖程序 */ 2 class DeadLock implements Runnable { 3     private boolean flag; 4     DeadLock(boolean flag) { 5         this.flag = flag; 6     } 7     public void run() { 8         if (flag){ 9             while (true)10                 synchronized (MyLock.locka) {11                     System.out.println(Thread.currentThread().getName()+ "..if   locka....");12                     synchronized (MyLock.lockb){13                         System.out.println(Thread.currentThread().getName()+ "..if   lockb....");14                     }15                 }16         } else {17             while (true)18                 synchronized (MyLock.lockb) {19                     System.out.println(Thread.currentThread().getName()+ "..else  lockb....");20                     synchronized (MyLock.locka) {21                         System.out.println(Thread.currentThread().getName()+ "..else   locka....");22                     }23                 }24         }25     }26 }27 28 class MyLock {29     public static final Object locka = new Object();30     public static final Object lockb = new Object();31 }32 33 class DeadLockTest {34     public static void main(String[] args) {35         DeadLock a = new DeadLock(true);36         DeadLock b = new DeadLock(false);37 38         Thread t1 = new Thread(a);39         Thread t2 = new Thread(b);40         t1.start();41         t2.start();42     }43 }

五、線程間通信

 ?。ㄒ唬┚€程間通信

  多個線程在操作同一個資源,但是操作的動作不同。

  (二)等待喚醒機制

  1、涉及的方法

 ?。?)wait(): 讓線程處于凍結狀態,被wait的線程會被存儲到線程池中。

  (2)notify():喚醒線程池中一個線程(任意)。

 ?。?)notifyAll():喚醒線程池中的所有線程。

  2、為什么這些方法都必須定義在同步中?

  因為這些方法是用于操作線程狀態的方法,必須要明確到底操作的是哪個鎖上的線程。

  3、為什么操作線程的方法wait、notify、notifyAll定義在了Object類中?

  因為這些方法是監視器的方法。監視器其實就是鎖,鎖可以是任意的對象,任意的對象調用的方式一定定義在Object類中。

  4、wait 和 sleep 區別?

 ?。?)wait可以指定時間也可以不指定;而sleep必須指定時間。

 ?。?)在同步中時,各自對CPU的執行權和鎖的處理不同:① wait釋放執行權,釋放鎖。② sleep釋放執行權,不釋放鎖。

  5、為什么要定義notifyAll ?

  因為在需要喚醒對方線程時。如果只用notify,容易出現只喚醒本方線程的情況。導致程序中的所有線程都等待。

 1 * 練習:多線程通信示例(生產者——消費者) 2 class Resource{ 3     private String name; 4     private int count = 1; 5     private boolean flag = false; 6     public synchronized void set(String name){ 7         while(flag) 8             try{this.wait();}catch(InterruptedException e){}         9         this.name = name + count;10         count++;11         System.out.println(Thread.currentThread().getName()+"...生產者..."+this.name);12         flag = true;13         notifyAll();14     }15 16     public synchronized void out(){17         while(!flag)18             try{this.wait();}catch(InterruptedException e){}19         System.out.println(Thread.currentThread().getName()+"...消費者........"+this.name);20         flag = false;21         notifyAll();22     }23 }24 25 class Producer implements Runnable{26     private Resource r;27     Producer(Resource r){28         this.r = r;29     }30     public void run(){31         while(true){32             r.set("烤鴨");33         }34     }35 }36 37 class Consumer implements Runnable{38     private Resource r;39     Consumer(Resource r){40         this.r = r;41     }42     public void run(){43         while(true){44             r.out();45         }46     }47 }48 49 class  ProducerConsumerDemo{50     public static void main(String[] args){51         Resource r = new Resource();52         Producer pro = new Producer(r);53         Consumer con = new Consumer(r);54 55         Thread t0 = new Thread(pro);56         Thread t1 = new Thread(pro);57         Thread t2 = new Thread(con);58         Thread t3 = new Thread(con);59         t0.start();60         t1.start();61         t2.start();62         t3.start();63     }64 }

  (三)關于同步和鎖的JDK1.5新特性

  1、jdk1.5以后將同步和鎖封裝成了對象,并將操作鎖的隱式方式定義到了該對象中,將隱式動作變成了顯示動作。

  2、Lock接口:替代了同步代碼塊或同步函數,將同步的隱式鎖操作變成顯式的鎖操作。同時更為靈活,可以一個鎖上加上多組監視器。

  3、Condition接口:替代了Object中的wait()、notify()、 notifyAll()方法,將這些監視器方法單獨進行了封裝,變成Condition監視器對象。Condition對象可以和任意鎖進行組合。

  4、接口Lock上的一些方法

 ?。?)lock():獲取鎖。

 ?。?)unlock():釋放鎖,通常需要定義finally代碼塊中。

  5、接口Condition上的一些方法

  (1)await():使當前線程在接到信號或被中斷之前一直處于等待狀態。

  (2)signal():喚醒一個等待線程。

 ?。?)signalAll():喚醒所有等待線程。

  1 練習:多生產者多消費者問題(JDK1.5解決辦法)  2 import java.util.concurrent.locks.*;  3 class Resource2  4 {  5     private String name;  6     private int count = 1;  7     private boolean flag = false;  8   9     //    創建一個鎖對象。 10     Lock lock = new ReentrantLock();     11  12     //通過已有的鎖獲取兩組監視器,一組監視生產者,一組監視消費者。 13     Condition producer_con = lock.newCondition(); 14     Condition consumer_con = lock.newCondition(); 15      16     public  void set(String name) 17     { 18         lock.lock(); 19         try 20         { 21             while(flag) 22             try{producer_con.await();}catch(InterruptedException e){} 23          24             this.name = name + count; 25             count++; 26             System.out.println(Thread.currentThread().getName()+"...生產者5.0..."+this.name); 27             flag = true; 28             consumer_con.signal(); 29         } 30         finally 31         { 32             lock.unlock(); 33         } 34          35     } 36  37     public  void out() 38     { 39         lock.lock(); 40         try 41         { 42             while(!flag) 43             try{consumer_con.await();}catch(InterruptedException e){} 44             System.out.println(Thread.currentThread().getName()+"...消費者.5.0......."+this.name); 45             flag = false; 46             producer_con.signal(); 47         } 48         finally 49         { 50             lock.unlock(); 51         } 52          53     } 54 } 55  56 class Producer2 implements Runnable 57 { 58     private Resource r; 59     Producer2(Resource r) 60     { 61         this.r = r; 62     } 63     public void run() 64     { 65         while(true) 66         { 67             r.set("烤鴨"); 68         } 69     } 70 } 71  72 class Consumer2 implements Runnable 73 { 74     private Resource r; 75     Consumer2(Resource r) 76     { 77         this.r = r; 78     } 79     public void run() 80     { 81         while(true) 82         { 83             r.out(); 84         } 85     } 86 } 87  88 class  ProducerConsumerDemo2 89 { 90     public static void main(String[] args)  91     { 92         Resource r = new Resource(); 93         Producer2 pro = new Producer2(r); 94         Consumer2 con = new Consumer2(r); 95  96         Thread t0 = new Thread(pro); 97         Thread t1 = new Thread(pro); 98         Thread t2 = new Thread(con); 99         Thread t3 = new Thread(con);100         t0.start();101         t1.start();102         t2.start();103         t3.start();104     }105 }

六、停止線程

 ?。ㄒ唬┩V咕€程的方式

   1、stop方法(已過時)。

   2、run方法結束。

 ?。ǘ﹔un()方法結束線程

  1、“定義標記方式”結束線程任務

  怎么控制線程的任務結束呢? 開啟多線程運行,運行代碼通常都會有循環結構,只要控制住循環就可以讓run方法結束,也就是線程結束。控制循環通常就用“定義標記”來完成。

  2、“interrupt()方法”結束線程任務

  如果線程處于了凍結狀態,無法讀取標記,如何結束呢?可以使用Thread對象的interrupt()方法將線程從凍結狀態強制恢復到運行狀態中來,讓線程具備CPU的執行資格,再通過操作標記,讓線程任務結束。但是強制動作會發生InterruptedException,記得要處理

  (三)線程停止示例

 1 class StopThread implements Runnable{ 2     private boolean flag = true; 3     public synchronized void run(){ 4         while(flag){ 5             try{ 6                 wait(); 7             }catch (InterruptedException e){ 8                 System.out.println(Thread.currentThread().getName()+"....."+e); 9                 flag = false;10             }            11             System.out.println(Thread.currentThread().getName()+"......++++");12         }13     }14     public void setFlag(){15         flag = false;16     }17 }18 19 class StopThreadDemo{20     public static void main(String[] args){21         StopThread st = new StopThread();22        Thread t1 = new Thread(st);23        Thread t2 = new Thread(st);24        t1.start();25        t2.setDaemon(true);//將該線程標記為守護線程(后臺線程), 該方法必須在啟動線程前調用。26        t2.start();27 28         int num = 1;29         for(;;){30             if(++num==50){31                 t1.interrupt();32                 break;33             }34             System.out.println("main...."+num);35         }36         System.out.println("over");37     }38 }

 ?。ㄋ模┦刈o線程(可理解為:后臺線程)

  void setDaemon(boolean on) :將該線程標記為守護線程或用戶線程,可以理解為后臺進程。

  守護線程的特點:

  1、該方法必須在啟動線程前調用。

  2、當正在運行的線程都是守護線程時,Java 虛擬機退出。

  3、當所有的前臺線程執行結束,后臺線程無論處于什么狀態,都自動結束。

 ?。ㄎ澹┚€程類的其他方法

  1、setPriority(int newPriority)方法(用來設置線程的優先級)

 ?。?)參數newPriority的取值有:MAX_PRIORITY(最高優先級10)、MIN_PRIORITY(最低優先級)、NORM_PRIORITY(默認優先級5)

  (2)格式:線程.setPriority(Thread. MAX_PRIORITY)

  2、join()方法

  當A線程執行到了b線程的.join()方法時,A線程就會等待,等B線程都執行完,A線程才會執行。(此時B和其他線程交替運行。)join可以用來臨時加入線程執行。

  3、yield()方法

  可以暫停當前線程,釋放執行權,讓其他線程執行。

  4、toString()方法

  返回該線程的字符串表示形式,包括線程名稱、優先級和線程組。

七、面試題

  (一)試題1:以下代碼,如果錯誤 錯誤發生在哪一行?  

1 class Test implements Runnable{2       public void run(Thread t)3       {4 5       }6 }

  分析:錯誤在第一行,應該被abstract修飾

 ?。ǘ┰囶}2:以下代碼的運行結果是什么?深入分析。

 1 class ThreadTest{ 2     public static void main(String[] args){ 3         new Thread(new Runnable(){ 4             public void run(){ 5                 System.out.println("runnable run"); 6             } 7         }) 8         { 9             public void run(){10                 System.out.println("subThread run");11             }12         }.start();13     }14 }

  運行結果:subThread run。

  分析:首先當然是以子類為主,所以先執行new Thread(){&hellip;…..}子類內容。如果子類方法沒有內容,再以任務為主,即運行new Runnable(){public void run(){………}}。如果任務都沒有,以Thread自己的run方法為主。

---------- android培訓、java培訓、期待與您交流! ----------


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 久久久久一区二区三区 | 蜜桃91麻豆 | 亚洲特黄 | 一二区成人影院电影网 | 毛片在线视频观看 | 欧美性色生活片免费播放 | 羞羞电影在线观看 | 精品一区二区久久久久 | 黄色av免费网站 | 91精品欧美一区二区三区 | 热久久91 | 久久久日韩精品一区二区 | 久久精品网址 | 欧美精品欧美极品欧美激情 | 中文字幕视频在线播放 | 毛片一级免费看 | 欧美视屏一区二区 | 国产毛片毛片 | 性 毛片 | 5xsq在线视频 | 精品一区二区三区免费毛片 | 99国产精品欲a | 国产精品一区2区3区 | 欧美亚洲黄色 | 日本在线高清 | 91成人午夜性a一级毛片 | 亚洲第一页综合 | 久色网站| 操操操操操 | 久久亚洲第一 | 成人激情在线观看 | 成人毛片网站 | 色人阁在线视频 | 欧美精品18 | 欧美性受xxx黑人xyx性爽 | 黄色网址免费在线播放 | 欧美日比视频 | 国产精品久久久久久久四虎电影 | 欧美成年性h版影视中文字幕 | 草莓福利社区在线 | 2021av视频|