一,等待/通知機制 實現線程間的通信
舉個例子:我們去飯店就餐,飯店上餐的時間不確定,如果我們一直去詢問前臺,是不是很煩,我麼這時就處於等待(wait)狀態,但是 飯店肯定會有人肯定會通知 (notify),那個桌的菜已經做好了,前臺就會通知這桌的人,菜來了。
1,主要的方法wait() /notify() 這個兩個方法時Object類本地方法 都要在同步方法中調用
1),wait() 使執行當前代碼的線程進行等待,執行完畢後,線程就會釋放鎖,以便別的線程可以獲得鎖。
2),notify(),隨機挑選一個處於wait()的線程,讓他獲得鎖,但這個方法不會立即釋放當前線程的對象鎖,只有程序執行完畢後,纔會釋放鎖。
3)sleep()方法,這個方法也是不是放鎖的 ,那麼別的線程要獲取這個鎖就會處於等待狀態。
4),在調用wait()方法後,使線程處於wait狀態,這時如果調用interrupt()就會出現InterrruptedException異常
5),notify()方法一次只會通知一個線程。notifyAll()通知所有的線程
二,使用等待/通知機制,實現生產者/消費者模式
/**
* 通知等待機制 實現消費者者/生產者模式
*/
public class PCDemo {
//消費品
public static class Data{
public static String value = " ";
}
//生產者
public static class Producer{
private String lock;
public Producer(String value){
this.lock = value;
}
public void setValue() throws Exception{
synchronized (lock){
if (!Data.value.equals(" ")){ //說明value沒有被消費
lock.wait();
}else{
String s = System.currentTimeMillis()+" ";
System.out.println("設置的value = " + s+" 當前線程的Name = "+Thread.currentThread().getName());
Data.value = s;//生產數據
lock.notify();//通知等待的消費者去消費
}
}
}
}
//消費者
public static class Consumer{
private String lock;
public Consumer(String lock){
this.lock = lock;
}
public void getValue() throws Exception{
synchronized (lock){
if (Data.value.equals(" ")){ //說明生產者沒有生產數據
lock.wait();//消費者等待
}else {
System.out.println("消費者獲得的值 "+ Data.value+" 當前線程的Name = "+Thread.currentThread().getName());
Data.value = " ";//模擬把數據消費了
lock.notify();//通知生產者趕快生產數據 ,我已經消費完了
}
}
}
}
//生產者線程
public static class ProThread implements Runnable{
private Producer producer;
public ProThread(Producer p){
this.producer = p;
}
@Override
public void run() {
while (true){
try {
producer.setValue();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
//消費者線程
public static class ConThread implements Runnable{
private Consumer consumer;
public ConThread(Consumer consumer){
this.consumer = consumer;
}
@Override
public void run() {
try {
while (true){
consumer.getValue();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception{
String lock = new String(" ");//使用同一個鎖對象
Thread p = new Thread(new ProThread(new Producer(lock)));
Thread c = new Thread(new ConThread(new Consumer(lock)));
p.setName("Producer");
c.setName("Consumer");
p.start();
c.start();
}
}
運行部分截圖
這時一個生產者,一個消費者的情況,兩個線程交替執行。
若果有多個消費者,多個生產者,則有可能產生“假死”現象,就是所有的線程都處於waitting,因爲notify()方法一次只能使一個處於waitting狀態的線程掉起來。解決辦法使用notifiyAll()方法即可。
三,join():等待線程對象銷燬,一個主線程要等待子線程結束在自行結束。
1,join()內部其實使用的是wait(),
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
join()方法具有使線程排隊執行的作用,類似與同步的效果,但和synchroized的原理不同,後者使用 “對象監視器”原理