多執行緒基礎4 同步與通訊
阿新 • • 發佈:2018-11-09
1.什麼情況下需要同步 當多執行緒併發執行同一程式碼時 希望某一段程式碼執行的過程中CPU不要切換到其他執行緒工作. 這時就需要同步.
2.同步程式碼塊 使用synchronized關鍵字加上一個鎖物件來定義一段程式碼, 這就叫同步程式碼塊 多個同步程式碼塊如果使用相同的鎖物件, 那麼他們就是同步的
public class TestMain { public static void print1() { //鎖物件可以是任意物件,但是被鎖的程式碼需要保證是同一把鎖,不能用匿名物件 synchronized(String.class){ System.out.print("1"); System.out.print("2"); } } //非靜態同步函式的鎖是:this (非靜態方法已建立例項化物件後呼叫的) public synchronized void print2() { System.out.print("1"); System.out.print("2"); } // 靜態的同步函式的鎖是:位元組碼物件.class (靜態是初始化時形成的所以 是位元組碼物件) public static synchronized void print3() { System.out.print("1"); System.out.print("2"); } }
多執行緒(兩個執行緒間的通訊)
- 1.什麼時候需要通訊 * 多個執行緒併發執行時, 在預設情況下CPU是隨機切換執行緒的 * 如果我們希望他們有規律的執行, 就可以使用通訊, 例如每個執行緒執行一次列印
- 2.怎麼通訊 * 如果希望執行緒等待, 就呼叫wait() * 如果希望喚醒等待的執行緒, 就呼叫notify(); * 這兩個方法必須在同步程式碼中執行, 並且使用同步鎖物件來呼叫
public class Demo1_Notify { /** * @param args * 等待喚醒機制 */ public static void main(String[] args) { final Printer p = new Printer(); new Thread() { public void run() { while(true) { try { p.print1(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while(true) { try { p.print2(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } //等待喚醒機制 class Printer { private int flag = 1; public void print1() throws InterruptedException { synchronized(this) { if(flag != 1) { this.wait(); //當前執行緒等待 } System.out.print("方法一"); System.out.print("\r\n"); flag = 2; this.notify(); //隨機喚醒單個等待的執行緒 } } public void print2() throws InterruptedException { synchronized(this) { if(flag != 2) { this.wait(); } System.out.print("方法2"); System.out.print("\r\n"); flag = 1; this.notify(); } } }
多執行緒(三個或三個以上間的執行緒通訊)
多個執行緒通訊的問題 * notify()方法是隨機喚醒一個執行緒 * notifyAll()方法是喚醒所有執行緒 * JDK5之前無法喚醒指定的一個執行緒 * 如果多個執行緒之間通訊, 需要使用notifyAll()通知所有執行緒, 用while來反覆判斷條件
public class Demo2_NotifyAll { /** * @param args */ public static void main(String[] args) { final Printer2 p = new Printer2(); new Thread() { public void run() { while(true) { try { p.print1(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while(true) { try { p.print2(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while(true) { try { p.print3(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } }
1,在同步程式碼塊中,用哪個物件鎖,就用哪個物件呼叫wait方法
2,為什麼wait方法和notify方法定義在Object這類中?
因為鎖物件可以是任意物件,Object是所有的類的基類,所以wait方法和notify方法需要定義在Object這個類中
3,sleep方法和wait方法的區別?
a,sleep方法必須傳入引數,引數就是時間,時間到了自動醒來wait方法可以傳入引數也可以不傳入引數,傳入引數就是在引數的時間結束後等待,不傳入引數就是直接等待
b,sleep方法在同步函式或同步程式碼塊中,不釋放鎖,睡著了也抱著鎖睡wait方法在同步函式或者同步程式碼塊中,釋放鎖
class Printer2 {
private int flag = 1;
public void print1() throws InterruptedException {
synchronized(this) {
while(flag != 1) {
this.wait(); //當前執行緒等待
}
System.out.print("方法一");
System.out.print("\r\n");
flag = 2;
//this.notify(); //隨機喚醒單個等待的執行緒
this.notifyAll();
}
}
public void print2() throws InterruptedException {
synchronized(this) {
while(flag != 2) {
this.wait(); //執行緒2在此等待
}
System.out.print("方法2");
System.out.print("\r\n");
flag = 3;
//this.notify();
this.notifyAll();
}
}
public void print3() throws InterruptedException {
synchronized(this) {
while(flag != 3) {
this.wait(); //執行緒3在此等待,if語句是在哪裡等待,就在哪裡起來
//while迴圈是迴圈判斷,每次都會判斷標記
}
System.out.print("方法三");
System.out.print("\r\n");
flag = 1;
//this.notify();
this.notifyAll();
}
}
}
消費者與生產者
//消費者
synchronized(物件) {
if(貨物==null)
物件.wait();
對應的處理邏輯
}
//生產者
synchronized(物件) {
產生貨物
物件.notifyAll();
}
執行緒間的通訊還可以使用 管道輸入/輸出流
public class Piped {
public static void main(String[] args) throws Exception {
PipedWriter out = new PipedWriter();
PipedReader in = new PipedReader();
// 將輸出流和輸入流進行連線,否則在使用時會丟擲IOException
out.connect(in);
Thread printThread = new Thread(new Print(in), "PrintThread");
printThread.start();
int receive = 0;
try {
while ((receive = System.in.read()) != -1) {
out.write(receive);
}
} finally {
out.close();
}
}
static class Print implements Runnable {
private PipedReader in;
public Print(PipedReader in) {
this.in = in;
}
public void run() {
int receive = 0;
try {
while ((receive = in.read()) != -1) {
System.out.print((char) receive);
}
} catch (IOException ex) {
}
}
}
}