等待通知機智的實現
方法wait()的作用是使當前執行代碼的線程進行等待,wait()方法是Object類的方法,該方法用來將當前線程置入“預執行隊列”中,並且在wait()所在的代碼行處停止執行,直到接到通知或被中斷爲止。
在調用wait()之前,線程必須獲得該對象的對象級別鎖,即只能在同步方法或同步塊中調用wait()方法。
在執行wait()方法後,當前線程釋放鎖。
如果調用wait()是沒有持有適當的鎖,則拋出IIegalMonitorStateException,它是RuntimeException的一個子類。
方法notify()也要在同步方法或同步代碼塊中調用,即在調用前,線程也必須獲得該對象的對象級別鎖。
該方法用來通知那些可能等待該對象的對象鎖的其他線程,如果有多個線程等待,則由線程規劃器隨機挑選出其中一個呈wait狀態的線程,對其發出通知notify,並使它等待獲取該對象的對象鎖。
需要說明的是,在執行notify()方法後,當前線程不會馬上釋放該對象鎖,呈wait狀態的線程也並不能馬上獲取該對象鎖,要等到執行notify()方法的線程將程序執行完,也就是退出synchronized代碼塊後,當前線程纔會釋放鎖,而呈wait狀態的線程纔可以獲取該對象的鎖。
當第一個獲得了該對象鎖的wait線程運行完畢後,他會釋放掉該對象鎖,此時如果該對象沒有再次使用notify語句,則即便該對象已經空閒,其他wait狀態等待的線程由於沒有得到該對象的通知,還會繼續阻塞在wait狀態,直到這對象發出一個notify或notifyall.
用一句話來總結一下wait和notify:
wait使線程停止運行,而notify使停止的線程繼續運行。
以上說法創建測試項目
wait_notify_size5
創建MyList類
public class MyList {
private static List list = new ArrayList();
public static void add() {
list.add("anything");
}
public static int size() {
return list.size();
}
}
類ThreadA
public class ThreadA extends Thread {
private Object lock;
public ThreadA(Object lock){
super();
this.lock = lock;
}
@Override
public void run() {
try {
synchronized (lock){
if(MyList.size() != 5){
System.out.println("wait begin"+System.currentTimeMillis());
lock.wait();
System.out.println("wait end"+ System.currentTimeMillis());
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
類ThreadB
public class ThreadB extends Thread {
private Object lock;
public ThreadB(Object lock) {
super();
this.lock = lock;
}
@Override
public void run() {
try {
synchronized (lock) {
for (int i = 0; i < 10; i++) {
MyList.add();
if (MyList.size() == 5) {
lock.notify();
System.out.println("已發出通知");
}
System.out.println("添加了" + (i + 1) + "個元素!");
Thread.sleep(1000);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
類Run
public class Run {
public static void main(String[] args) {
try {
Object lock = new Object();
ThreadA a = new ThreadA(lock);
a.start();
Thread.sleep(50);
ThreadB b = new ThreadB(lock);
b.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
運行結果如下
日誌信息 wait end 在最後輸出,這也說明notify()方法執行後並不是立即釋放鎖。
關鍵字synchronized可以將任何一個Object對象作爲同步對象來看待,而java爲每個對象都實現了wait()和notify()方法,它們必須用在被synchronized同步的Object的臨界區內。通過調用wait()方法可以使處於臨界區內的線程進入等待狀態,同時釋放被同步對象的鎖。而notify操作可以喚醒一個因調用了wait操作而處於阻塞狀態的線程,使其進入就緒狀態。被重新喚醒的線程會試圖重新獲得臨界區的控制權,也就是鎖,並繼續執行臨界區內wait之後的代碼。
如果發出的notify操作時沒有處於阻塞狀態中的線程,那麼該命令就會被忽略。
wait()方法可以使用調用該方法的線程釋放共享資源的鎖,然後從運行狀態退出,進入等待隊列,直到被再次喚醒。
notify()方法可以隨機喚醒等待隊列中等待同一共享資源的“一個”線程,並是該線程退出等待隊列,進入可運行狀態,也就是notify()方法僅通知“一個”線程。
notifyAll()方法可以使所有正在等待隊列中等待同一共享資源的“全部”線程從等待狀態退出,進入可運行狀態。此時,優先級最高的那個線程最先執行,但也有可能是隨機執行,因爲這要取決於JVM虛擬機的實現。
方法notify()被執行後,不釋放鎖,必須執行完notify()方法所在的同步代碼塊synchronized代碼塊後才釋放鎖。
當線程呈wait()狀態時,調用線程對象的interrupt()方法會出現InterruptedException異常。
方法notify()僅隨機喚醒一個線程。
當多次調用notify()方法時,會隨機將等待wait狀態的線程進行喚醒。
notifyAll()喚醒所有的線程
wait(long)
方法wait(long)的功能是等待某一段時間內是否有線程對鎖進行喚醒,如果超過這個時間則自動喚醒。
通過管道進行線程間通信:字節流
管道流是一種特殊的流,用於在不同線程間直接傳送數據。一個線程發送數據到輸出管道,另一個線程從輸入管道中讀數據。通過使用管道,實現不同線程間的通信,而無需藉助於累死臨時文件之類的東西。
在Java的jdk中提供了4個類來使線程間可以進行通信:
1)PipedInputStream和PipedOutputStream
2)PipedReader和PipedWriter
創建測試項目pipeInputOutPut
類ReadData
public class ReadData {
public void readMethod(PipedInputStream input){
try {
System.out.println("read :");
byte[] byteArray = new byte[20];
int readLength = input.read(byteArray);
while (readLength != -1){
String newData = new String(byteArray, 0, readLength);
System.out.println("讀取到:"+newData);
readLength = input.read(byteArray);
}
System.out.println();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
類WriteData
public class WriteData {
public void writeMethod(PipedOutputStream out){
try {
System.out.println("write :");
for (int i = 0; i < 300; i++) {
String outData = ""+(i+1);
out.write(outData.getBytes());
System.out.println("寫入:"+outData);
}
System.out.println();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
類ThreadRead
public class ThreadRead extends Thread {
private ReadData read;
private PipedInputStream input;
public ThreadRead(ReadData read, PipedInputStream input) {
super();
this.read = read;
this.input = input;
}
@Override
public void run() {
read.readMethod(input);
}
}
類ThreadWrite
public class ThreadWrite extends Thread {
private WriteData write;
private PipedOutputStream out;
public ThreadWrite(WriteData write, PipedOutputStream out){
super();
this.write = write;
this.out = out;
}
@Override
public void run() {
write.writeMethod(out);
}
}
類Run
public class Run {
public static void main(String[] args) {
try {
WriteData writeData = new WriteData();
ReadData readData = new ReadData();
PipedInputStream inputStream = new PipedInputStream();
PipedOutputStream outputStream = new PipedOutputStream();
//使兩個Stream之間產生通信鏈接,這樣纔可以將數據進行輸出與輸入
outputStream.connect(inputStream);
ThreadRead threadRead = new ThreadRead(readData, inputStream);
threadRead.start();
Thread.sleep(2000);
ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream);
threadWrite.start();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
運行結果
通過管道進行線程間通信:字符流
創建測試項目
pipeReaderWriter
類ReadData
public class ReadData {
public void readMethod(PipedReader input) {
try {
System.out.println("read: ");
char[] byteArray = new char[20];
int readLength = input.read(byteArray);
while (readLength != -1) {
String newData = new String(byteArray, 0, readLength);
System.out.println("讀數據:"+newData);
readLength = input.read(byteArray);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
類WriteData
public class WriteData {
public void writeMethod(PipedWriter out) {
try {
System.out.println("write: ");
for (int i = 0; i < 300; i++) {
String outData = "" + (i + 1);
out.write(outData);
System.out.println("寫入數據:"+outData);
}
System.out.println();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
類ThreadRead
public class ThreadRead extends Thread {
private ReadData read;
private PipedReader input;
public ThreadRead(ReadData read, PipedReader input) {
super();
this.read = read;
this.input = input;
}
@Override
public void run() {
read.readMethod(input);
}
}
類ThreadWrite
public class ThreadWrite extends Thread {
private WriteData write;
private PipedWriter out;
public ThreadWrite(WriteData write, PipedWriter out){
super();
this.write = write;
this.out = out;
}
@Override
public void run() {
write.writeMethod(out);
}
}
類Run
public class Run {
public static void main(String[] args) {
try {
WriteData writeData = new WriteData();
ReadData readData = new ReadData();
PipedReader inputStream = new PipedReader();
PipedWriter outputStream = new PipedWriter();
outputStream.connect(inputStream);
ThreadRead threadRead = new ThreadRead(readData, inputStream);
threadRead.start();
Thread.sleep(2000);
ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream);
threadWrite.start();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
運行結果
join()方法的使用
作用說明:
如果子線程中要進行大量的耗時運算,主線程往往早於子線程結束之前結束。這時,如果主線程想等待子線程執行完成之後再結束,比如子線程處理一個數據,主線程要取得這個數據中的值,就要用到join()方法了。
join()的作用就是等待線程對象銷燬。
創建測試項目
joinTest
創建類
MyThread
public class MyThread extends Thread {
@Override
public void run() {
try {
int secondValue = (int) (Math.random() * 10000);
System.out.println(secondValue);
Thread.sleep(secondValue);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
創建類
Test
public class Test {
public static void main(String[] args) {
try {
MyThread threadTest = new MyThread();
threadTest.start();
threadTest.join();
System.out.println("我想當ThreadTest對象執行完畢後我再執行,我做到了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
運行結果
結論:
方法join具有使線程排隊運行的作用,有些類似同步的運行效果。join與synchronized的區別是:join在內部使用wait()方法進行等待,而sychronized關鍵字使用的是“對象監視器”原理作爲同步。
join(long)參數是設定等待時間。
join(long)與sleep(long)的區別
- 方法join(long)的功能在內部是使用wait(long)方法來實現的,所以join(long)方法具有釋放鎖的特點。
- sleep(long)方法不釋放鎖
類ThreadLocal的使用
類ThreadLoacal主要解決的就是每個線程都綁定自己的值,可以將ThreadLocal類比喻成全局存放數據的盒子,盒子中可以存儲每個線程的私有數據。
類ThreadLocal解決的是變量在不同線程間的隔離性,也就是不同線程擁有自己的值,不同線程中的值可以放入ThreadLocal類中進行保存的。