黑馬程式設計師--第十一天:多執行緒
阿新 • • 發佈:2019-01-22
---------------------- ASP.Net+Android+IO開發S、.Net培訓、期待與您交流! ----------------------
/*11-1 程序: 是一個正在執行中的程式。 每一個程序執行都有一個執行順序,該順序是一個執行路徑,或者叫一個單元。 執行緒:就是程序中的一個獨立的控制單元。 執行緒在控制著程序的執行。 一個程序中至少有一個執行緒。 java vm 啟動時會有一個程序java.exe 該程序中至少有一個執行緒負責java程式的執行。 而且這個執行緒執行的程式碼存在於main方法中。 該執行緒稱之為主執行緒 擴充套件:其實更細節的說明jvm,jvm啟動不止一個執行緒,還有負責垃圾回收機制的執行緒。 */ class { public static void main(String[] args) { System.out.println("Hello World!"); } } /*11-2 如何在自定義的程式碼中,自定義一個執行緒? 通過api查詢,java已經提供了對執行緒這類食物的描述。就是Thread。 建立執行緒的第一種方式:繼承Thread 步驟: 1.定義類,繼承Thread 2.複寫Thread的run方法 目的:將自定義程式碼儲存在run方法,讓執行緒執行。 3.呼叫執行緒的start方法。 該方法兩個作用:啟動執行緒,呼叫run方法。 發現執行結果每次都不同。 因為多個執行緒都獲取cpu的執行權。cpu執行到誰,誰就執行。 明確一點,在某一時刻,只能有一個程式在執行。(多核除外) cpu 在做著快速切換,以達到看上去是同時執行的效果。 我們可以形象把多執行緒的執行行為在互相搶奪cpu的執行權。 這就是多執行緒的一個特性:隨機性。誰搶到,誰執行。至於執行多長,cpu說了算。 */ class Demo extends Thread { public void run(){ for(int i = 0; i < 60; i++) System.out.println("hello"); } } class { public static void main(String[] args) { Demo d = new Demo; d.start(); //d.run(); for (int i=0;i<60 ;i++ ) System.out.println("Hello World!"); } } /*11-3 為什麼要覆蓋run方法? Thread類用於描述執行緒 該類定義了一個功能,用於儲存執行緒要執行的程式碼,該功能就是run方法。 也就是說Thread類中的run方法,用於儲存執行緒要執行的程式碼。 */ /* 11-6 獲取執行緒以及名稱 執行緒都有自己預設的名稱 Thread-編號 該編號從0開始。 static Thread CurrentThread();獲取當前執行緒物件。 getName(); 獲取執行緒名稱。 設定執行緒名稱:setName或者建構函式。 Thread.currentThread()=this;(this.getName()); */ class { public static void main(String[] args) { System.out.println("Hello World!"); } } /*11-7 需求:簡單的賣票程式。 多個視窗賣票。 */ /*11-8 實現Runnable介面 步驟: 1.定義類實現Runnable介面。 2.覆蓋Runnable介面中的run方法。 將執行緒要執行的程式碼存放在該run方法中。 3.通過Thread類建立執行緒物件。 4.將Runnable介面的子類物件作為實際引數傳遞給Thread類的建構函式。 為什麼要將Runnable介面的子類物件傳遞給Thread的建構函式? 因為,自定義的run方法所屬的物件是Runnable介面的子類,所以要讓執行緒 去指定物件的run方法。 5.呼叫Thread類的start方法開啟執行緒並呼叫Runnable介面子類的run方法。 實現方式和家長方式有什麼區別? 實現的好處:避免了單繼承的侷限性(可以繼承其他類,不只是Thread) 在定義執行緒是,建立使用實現方式。 兩種方式的區別: 繼承Thread: 執行緒程式碼存放Thread子類run方法中。 實現Runnable:執行緒程式碼存放介面子類的run方法。 */ /*11-9 多執行緒安全問題 通過分析,發現打印出0,-1,-2等票 多執行緒 的執行出現安全問題。 */ class Ticket implements Runnable //extends Thread //因為Thread有實現Runnable介面, //所以不實現Runnable介面也可以多個視窗賣同一組票, //直接用extends Thread, new Thread(new Ticket(){覆蓋run方法}).start(). //區別在與實現Runnable可以繼承其他類 { private int tick = 100; public void run(){ while (tick>0) { System.out.println(Thread.currentThread().getName()+"sale: " + tick--); } } } class Demo { public static void main(String[] args) { Ticket t1 = new Ticket(); //Ticket t2 = new Ticket(); //Ticket t3 = new Ticket(); new Thread(t1).start(); new Thread(t1).start(); new Thread(t1).start(); //t1.start(); //t2.start(); //t3.start(); //t1.run(); //t2.run(); //t3.run(); System.out.println("Hello World!"); } } /*11-9 多執行緒安全問題 通過分析,發現打印出0,-1,-2等票 多執行緒 的執行出現安全問題。 問題原因: 當多條語句在操作同一個執行緒共享資料時,一個執行緒對多條語句只執行了一部分,還沒有執行完。 另一個執行緒參與進來,導致共享資料的錯誤。 解決方法: 對多條操作共享資料的語句,只能讓一個執行緒執行完。在執行過程中,其他執行緒不可以參與執行。 */ /*11-10:同步程式碼塊 java對於多執行緒安全問題提供了專業的解決方式。 就是同步程式碼塊。 synchronized(物件){ 需要被同步的程式碼; } 物件如同鎖,持有鎖的執行緒可以在同步中執行。 沒有持有鎖的執行緒即使獲取cpu的執行權也進不去,因為沒有獲取鎖。 火車上的衛生間 同步的前提: 1.必須要有2個或者2個以上的執行緒。 2.必須是多個執行緒使用同一個鎖。 必須保證同步中只有一個執行緒在執行。 好處:解決了多執行緒的安全問題。 弊端:多個執行緒需要判斷鎖,較為消耗資源。 */ class Ticket implements Runnable { private int tick = 100; public void run(){ while (true) { synchronized(new Object()){ if (tick>0) { Thread.sleep(10); System.out.println(Thread.currentThread().getName()+"sale: " + tick--); }else break; } } } } class Demo { public static void main(String[] args) { Ticket t1 = new Ticket(); //Ticket t2 = new Ticket(); //Ticket t3 = new Ticket(); new Thread(t1).start(); new Thread(t1).start(); new Thread(t1).start(); //t1.start(); //t2.start(); //t3.start(); //t1.run(); //t2.run(); //t3.run(); System.out.println("Hello World!"); } } /*11-11 需求: 銀行有一個金庫。 有兩個儲戶分別存300元,每次存100,存3次。 目的:該程式是否有安全問題,如果有,如何解決? 如何找問題: 1.明確哪些程式碼是多執行緒執行程式碼。 2.明確共享資料。 3.明確多執行緒執行程式碼中哪些語句是操作共享資料的? */ class Bank { private int sum; public synchronized void add(int n){ //synchronized(new Object()){ sum = sum + n; try{Thread.sleep(10);}catch(Exception e){} System.out.println("Sum="+sum); //} } } class Cus implements Runnable { private Bank b = new Bank(); public void run(){ for (int x=0;x<3 ;x++ ) { b.add(100); } } } class BankDemo { public static void main(String[] args) { Cus c = new Cus(); Thread t1 = new Thread(c); Thread t2 = new Thread(c); t1.start(); t2.start(); System.out.println("Hello World!"); } } /*11-12 同步函式用的是哪一個鎖? 函式需要被物件定義。那麼函式都有一個所屬物件引用。就是this。 所以同步函式所用的鎖是this。 通過該程式進行驗證。 為了驗證方便,所用2個執行緒來賣票 一個執行緒在同步程式碼塊中 一個執行緒在同步函式中 都在執行賣票動作。 */ /*11-13靜態同步函式的鎖是Class物件。 如果同步函式被靜態修飾後,使用的鎖是什麼呢? 通過驗證,發現不是this,因為靜態方法中也不可以定義this。 靜態進記憶體時,記憶體中沒有本類物件,但是一定有該類對應的位元組碼檔案物件。 類名.class 該物件的型別是Class。 靜態的同步方法,使用的鎖是該方法所在的類的位元組碼檔案物件。類名.class */ class Ticket implements Runnable { boolean flag = true; private int tick = 100; Object obj = new Object(); public void run(){ if (flag) { while (true) { //Ticket.class synchronized(obj){//當該引數寫new Object時,每個執行緒進來都持有不同的物件鎖 if (tick>0) { try{Thread.sleep(10);}catch (Exception e){} System.out.println(Thread.currentThread().getName()+"sale: "+"t--" + tick--); }else break; } } }else{ while(true){ show(); if(tick<=0) break; } } } synchronized void show(){//static if (tick>0) { try{Thread.sleep(10);}catch (Exception e){} System.out.println(Thread.currentThread().getName()+"sale: " +"s.."+ tick--); } } } class Demo { public static void main(String[] args) throws Exception { Ticket t1 = new Ticket(); //Ticket t2 = new Ticket(); //Ticket t3 = new Ticket(); new Thread(t1).start(); Thread.sleep(10); //t1.flag = false; new Thread(t1).start(); new Thread(t1).start(); //t1.start(); //t2.start(); //t3.start(); //t1.run(); //t2.run(); //t3.run(); System.out.println("Hello World!"); } } /*11-14 單例設計模式。 */ //餓漢式: class Single{ private static final Single s = new Single(); private Single(){}; public static Single getInstance(){ return s; } } //懶漢式 class Single{ private static Single s = null; private Single(){} public synchronized static Single getInstance(){ if (s == null) s = new Single(); return s; } } class Single{ private static Single s = null; private Single(){} public static Single getInstance(){ if (s == null) { synchronized(Single.class){//該類所屬的位元組碼檔案物件 if (s == null) //-->A //-->B s = new Single(); } } return s; } } /* 懶漢式和餓漢式有什麼不同? 懶漢式的特點在於例項的延遲載入。 懶漢式延遲載入的問題:多執行緒訪問時存在安全問題。 怎麼解決? 可以加同步來解決。用同步程式碼塊和同步函式都行。 但是有些低效,用雙重判斷能解決效率問題 加同步是使用的鎖是哪一個? 該類所屬的位元組碼檔案物件。 */ class { public static void main(String[] args) { System.out.println("Hello World!"); } } /*11-15 死鎖: */ class Ticket implements Runnable { boolean flag = true; private int tick = 100; Object obj = new Object(); public void run(){ if (flag) { while (true) { //Ticket.class synchronized(obj){//當該引數寫new Object時,每個執行緒進來都持有不同的物件鎖 if (tick>0) show(); else break; } } }else{ while(true){ show(); if(tick<=0) break; } } } synchronized void show(){//static synchronized(obj){//當該引數寫new Object時,每個執行緒進來都持有不同的物件鎖 if (tick>0) { try{Thread.sleep(10);}catch (Exception e){} System.out.println(Thread.currentThread().getName()+"sale: "+"t--" + tick--); } } } } class Demo { public static void main(String[] args) throws Exception { Ticket t1 = new Ticket(); //Ticket t2 = new Ticket(); //Ticket t3 = new Ticket(); new Thread(t1).start(); Thread.sleep(10); //t1.flag = false; new Thread(t1).start(); new Thread(t1).start(); System.out.println("Hello World!"); } } //------------------------------------- class Test implements Runnable { private boolean flag; Test(boolean flag){ this.flag = flag; } public void run(){ if (flag) { synchronized(MyLock.locka){ System.out.println("if locka"); synchronized(MyLock.lockb){ System.out.println("if lockb"); } } }else{ synchronized(MyLock.lockb){ System.out.println("else lockb"); synchronized(MyLock.locka){ System.out.println("else locka"); } } } } } class MyLock { static Object locka = new Object(); static Object lockb = new Object(); } class DeadLockTest { public static void main(String[]args){ Thread t1 = new Thread(new Test(true)); Thread t2 = new Thread(new Test(false)); t1.start(); t2.start(); } } //個人死鎖的例子---------------------------- public class Demo { public static void main(String[] args) { Count c = new Count(); System.out.println("start"); new Thread(new CountA(c)).start(); new Thread(new CountB(c)).start(); } } class Count{ int a = 0; int b = 0; public int countA(){ synchronized(Count.class){ try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("countA counted"); countC(); return ++a; } } public int countB(){ synchronized(Count.class){ try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } countA(); return ++b ; } } public int countC(){ synchronized(Count.class){ try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("countA counted"); return ++a; } } } class CountA implements Runnable{ Count c = null; CountA(Count c){ this.c = c; } @Override public void run() { // TODO Auto-generated method stub for(int i =0; i<10;i++){ int num = c.countA() ;//+ c.countB(); System.out.println("countA = "+num); } } } class CountB implements Runnable{ Count c = null; CountB(Count c){ this.c = c; } @Override public void run() { // TODO Auto-generated method stub for(int i =0; i<10;i++){ int num = c.countB() ;//+ c.countA(); System.out.println("countB = "+num); } } }
---------------------- ASP.Net+Android+IO開發S、.Net培訓、期待與您交流! ----------------------