標籤:set 不能 避免 另一個 print 方法 name 代碼 儲存
1.理清概念 並行與並發: *並行:多個cpu執行個體或者多台機器同時執行一段處理邏輯,是真正的同時。 *並發:通過cpu調度演算法,讓使用者看上去同時執行,實際上從cpu操作層面不是真正的同時。並發往往在情境中有公用的資源,那麼針對這個公用的資源往往產生瓶頸,我們會用TPS或者QPS來反應這個系統的處理能力。 2.線程建立 * 繼承thread類建立多線程 public class Example01 { public static void main(String[] args) { MyThread myThread = new MyThread(); //建立線程 MyThread 的線程對象 myThread.start(); //開啟線程 while(true){ System.out.println("main 方法在運行"); } } } class MyThread extends Thread{ public void run(){ while (true){ System.out.println("MyThread 類的 run()方法在運行"); } } } * 實現Runnable介面建立線程 public class Example01 { public static void main(String[] args) { MyThread myThread = new MyThread(); //建立線程 MyThread 的線程對象 Thread thread = new Thread(myThread); //建立線程對象 thread.start(); //開啟線程,執行線程中的run()方法 while(true){ System.out.println("main 方法在運行"); } } } class MyThread implements Runnable{ public void run(){ while (true){ System.out.println("MyThread 類的 run()方法在運行"); } } } * 兩種實現多線程方式的對比分析,實現Runnable介面相對於繼承Thread類有如下顯著的好處 1.可以避免由於java的單繼承帶來的局限。java不支援多繼承(子類不能有多個父類),其他類的子類不能用繼承Thread類的方式,只能採用實現Runnable介面的方式 2.適合多個相同程式碼的線程去處理同一個資源的情況,把線程同程式碼、資料有效分離 * 後台進程 --如果某個線程對象在啟動之前調用了setDaemon(true)語句,這個線程就變成一個後台進程 --當前台線程結束後,JVM會通知後台線程 --進程中只有後台線程運行時,進程會結束 3.線程的調度 * 線程的優先順序 --static int MAX_PRIORITY 表示線程的最高優先順序 相當於值10 --static int MIN_PRIORITY 表示線程的最低優先順序 相當於值1 --static int NORM_PRIORITY 表示線程的普通優先順序 相當於值5 * 線程休眠 --Thread.sleep() * 線程讓步 --Thread.yield() * 線程插隊 --join() 4.多線程同步 * 安全執行緒 --多個線程同時去訪問同一個資源時,易引發安全問題。 *同步代碼塊 class Ticket1 implements Runnable{ private int tickets = 10; //定義變數 賦值 Object lock = new Object(); //定義任意一個對象,用作同步代碼塊的鎖 public void run(){ while (true){ synchronized(lock){ //定義同步代碼塊 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } if(tickets >0){ System.out.println(Thread.currentThread().getName()+"---賣出的票---"+tickets--); }else{ break; } } } } } public class Example01 { public static void main(String[] args) { Ticket1 ticket = new Ticket1(); //建立 Ticket1 對象 //建立並開啟四個線程 new Thread(ticket,"線程1").start(); new Thread(ticket,"線程2").start(); new Thread(ticket,"線程3").start(); new Thread(ticket,"線程4").start(); } } *同步方法 class Ticket1 implements Runnable{ private int tickets = 10; //定義變數 賦值 public void run(){ while (true){ saleTicket(); if(tickets <=0){ break; } } } //定義一個同步方法saleTicket() private synchronized void saleTicket(){ if(tickets > 0){ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"---賣出的票---"+tickets--); } } } public class Example01 { public static void main(String[] args) { Ticket1 ticket = new Ticket1(); //建立 Ticket1 對象 //建立並開啟四個線程 new Thread(ticket,"線程1").start(); new Thread(ticket,"線程2").start(); new Thread(ticket,"線程3").start(); new Thread(ticket,"線程4").start(); } } *鎖死問題 5.多線程通訊 *問題引入:假設有兩個線程同時去操作同一個儲存空間,其中一個線程負責向儲存空間中存入資料,另一個線程負責取出資料。 --資料存放區類 public class Storage { //資料存放區數組 private int[] cells = new int[10]; //inPos 表示存入時數組下標,outPos表示取出時數組下標 private int inPos,outPos; //定義一個put()方法向數組中存入資料 public void put(int num){ cells[inPos] = num; System.out.println("在cells["+inPos+"]中放入資料--"+cells[inPos]); inPos++; if(inPos == cells.length) inPos = 0; //當inPos為數組長度時 將其置為0 } //定義一個get()方法從數組中取出資料 public void get(){ int data = cells[outPos]; System.out.println("在cells["+outPos+"]中取出資料--"+data); outPos++; if(outPos == cells.length) outPos = 0; //當outPos為數組長度時 將其置為0 } } --資料存取類 public class Input implements Runnable { private Storage st; private int num; Input (Storage st){ //通過構造方法接收一個Storage對象 this.st= st; } @Override public void run() { while(true){ st.put(num++); //將num存入數組,每次存入後num自增 } } } public class Output implements Runnable { private Storage st; Output(Storage st){ //通過構造方法接收一個Storage對象 this.st = st; } @Override public void run() { while(true){ st.get(); } } } --建立線程 public class ExampleTest02 { public static void main(String[] args) { Storage st = new Storage(); //建立資料存放區對象 Input input = new Input(st); //建立Input對象傳入Storage對象 Output output=new Output(st); //建立Output對象傳入Storage對象 new Thread(input).start(); //開啟新線程 new Thread(output).start(); //開啟新線程 } } * 問題解決:測試發現上述存取進程是隨機的,未按照順序輪流執行。如需按照一定順序輪流執行,此時需要讓進程間進行通訊 --在Object類中提供了wait(),notify(),notifyAll()方法用於解決線程間的通訊問題,因為Java中的所有類都是Object類的子類或間接子類,因此任何類的執行個體對象都可以直接使用這些方法。 --void wait() 使當前線程放棄同步鎖並進入等待,直到其他線程進入此同步鎖,並調用notify()或notifyAll()方法喚醒該線程為止 --void notify() 喚醒此同步鎖上等待的第一個調用wait()方法的線程 --void notifyAll() 喚醒此同步鎖上調用wait()方法的所有線程 public class Storage { private int[] cells = new int[10]; //資料存放區數組 private int inPos,outPos; //inPos 表示存入時數組下標,outPos表示取出時數組下標 private int count; //存入或取出資料的數量 //定義一個put()方法向數組中存入資料 public synchronized void put(int num){ //存入資料如果等於cells的長度,此線程等待 while(count == cells.length){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } cells[inPos] = num; System.out.println("在cells["+inPos+"]中放入資料--"+cells[inPos]); inPos++; if(inPos == cells.length) inPos = 0; //當inPos為數組長度時 將其置為0 count++; this.notify(); } //定義一個get()方法從數組中取出資料 public synchronized void get(){ while(count == 0){ //如果count為0 此線程等待 try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } int data = cells[outPos]; System.out.println("在cells["+outPos+"]中取出資料:"+data); cells[outPos] = 0; outPos++; if(outPos == cells.length) outPos = 0; //當outPos為數組長度時 將其置為0 count--; this.notify(); } } 注意:如果wait(),notify(),notifyAll()方法調用者不是同步鎖對象,JVM會拋出 java.lang.IllegalMonitorStateException
j2ee(5) 線程