方法wait()的做用是使當前線程進行等待,wait()方法是Object類的方法,該方法用來將當前線程放到「預執行隊列」,並在wait()所在的代碼處中止執行,直到接到通知或中斷爲止。只能在同步方法或同步快中使用wait()方法,執行wait()後,當前線程釋放鎖。java
方法notify()也要在同步方法或同步快中調用,在調用前也必須得到該對象的的對象級別鎖。該方法用來通知那些可能等待該對象的對象鎖的其餘線程,若是有多個線程等待,則由線程規劃器隨機選出一個wait狀態的線程,對其發出notify通知,使他等待獲取對象鎖。git
在執行notify()後當前線程不會立刻釋放鎖,會在線程退出synchronized代碼塊纔會釋放鎖,呈wait狀態的線程才能夠獲取鎖。當第一個獲取對象鎖的wait線程運行結束釋放鎖後,若是該對象沒有再次notify,其餘wait狀態的線程依然會阻塞wait狀態,直到這個對象發出notify或notifyAll。github
public class MyWait {
private final Object lock;
MyWait(Object lock){
this.lock=lock;
}
public void waitTest(){
try {
synchronized (lock){
System.out.println("開始 wait time = " + System.currentTimeMillis());
lock.wait();
System.out.println("結束 wait time = " + System.currentTimeMillis());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
複製代碼
public class MyNotify {
private final Object lock;
MyNotify(Object lock){
this.lock=lock;
}
public void notifyTest(){
synchronized (lock){
System.out.println("開始 notify time = " + System.currentTimeMillis());
lock.notify();
System.out.println("結束 notify time = " + System.currentTimeMillis());
}
}
}
複製代碼
public class Main {
public static void main(String[] args){
try {
Object lock = new Object();
MyWait myWait = new MyWait(lock);
new Thread(() -> myWait.waitTest()).start();
Thread.sleep(3000);
MyNotify myNotify = new MyNotify(lock);
new Thread(() -> myNotify.notifyTest()).start();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
複製代碼
開始 wait time = 1552812964325
開始 notify time = 1552812967328
結束 notify time = 1552812967328
結束 wait time = 1552812967328
複製代碼
從輸出內容能夠看出3秒後執行notify方法,並在notify方法執行結束後才執行wait後的方法。bash
新建(new):新建立了一個線程對象。微信
可運行(runnable):線程對象建立後,其餘線程(好比main線程)調用了該對象的start()方法。該狀態的線程位於可運行線程池中,等待被線程調度選中,獲 取cpu的使用權。ide
運行(running):可運行狀態(runnable)的線程得到了cpu時間片(timeslice),執行程序代碼。ui
阻塞(block):阻塞狀態是指線程由於某種緣由放棄了cpu使用權,也即讓出了cpu timeslice,暫時中止運行。直到線程進入可運行(runnable)狀態,纔有 機會再次得到cpu timeslice轉到運行(running)狀態。阻塞的狀況分三種:this
(一). 等待阻塞:運行(running)的線程執行o.wait()方法,JVM會把該線程放 入等待隊列(waitting queue)中。spa
(二). 同步阻塞:運行(running)的線程在獲取對象的同步鎖時,若該同步鎖 被別的線程佔用,則JVM會把該線程放入鎖池(lock pool)中。.net
(三). **其餘阻塞**: 運行(running)的線程執行Thread.sleep(long ms)或t.join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時join()等待線程終止或者超時、或者I/O處理完畢時,線程從新轉入可運行(runnable)狀態。
複製代碼
本節代碼GitHub
在不少狀況,主線程建立並啓動子線程,若是子線程中進行大量的耗時運算,主線程每每將遭遇子線程結束以前結束。若是主線程要等待子線程執行完成以後在結束,就要使用join()方法,join()做用是等待線程對象銷燬。
Thread類除了提供join()方法以外,還提供了join(long millis)、join(long millis, int nanos)兩個具備超時特性的方法。這兩個超時方法表示,若是線程thread在指定的超時時間沒有終止,那麼將會從該超時方法中返回。
public class Main {
public static void main(String[] args) throws InterruptedException{
Thread thread = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName()+"正在執行");
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}, "線程1");
thread.start();
thread.join();
System.out.println("等待"+thread.getName()+"執行完");
}
}
// 輸出
線程1正在執行
等待線程1執行完
複製代碼
方法join(long)的功能是在內部使用wait(long)方法來實現的,因此join(long)方法具備釋放鎖的特色。二sleep(long)不會釋放鎖。
變量值共享可使用public static變量的形式,全部線程都使用同一個public static變量,若是想實現每一個線程都有本身的共享變量可使用ThreadLocal來解決。
ThreadLocal的相關方法:
public class ThreadLocalTeat {
public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException{
int count = 30;
String name = "Thread-";
for (int i=0; i<count; i++){
Thread thread = new Thread(() -> {
threadLocal.set(Thread.currentThread().getName());
System.out.println(threadLocal.get());
}, name+i);
thread.start();
}
Thread.sleep(20000);
}
}
// 輸出
Thread-0
Thread-4
Thread-3
Thread-6
Thread-2
Thread-1
Thread-7
。。。
複製代碼
使用類InheritableThreadLocal能夠在子線程中獲取父線程繼承下來的值。
public class InheritableThreadLocalTest extends InheritableThreadLocal {
@Override
protected Object childValue(Object parentValue) {
return super.childValue(parentValue);
}
@Override
protected Object initialValue() {
return System.currentTimeMillis();
}
}
複製代碼
* @date 2019/6/18 8:28
* @description
*/
public class InheritableTeat {
static public class Inner{
public static InheritableThreadLocalTest threadLocalTest = new InheritableThreadLocalTest();
}
public static void main(String[] args) throws InterruptedException{
for (int i = 0; i<3; i++){
System.out.println("在main線程中獲取值:"+ Inner.threadLocalTest.get());
}
for (int i=0; i<3; i++){
new Thread(() -> {
System.out.println("在"+Thread.currentThread().getName()+"中獲取值:"+ Inner.threadLocalTest.get());
}, "Thread-"+i).start();
}
Thread.sleep(1000);
}
}
// 輸出
在main線程中獲取值:1560818029616
在main線程中獲取值:1560818029616
在main線程中獲取值:1560818029616
在Thread-1中獲取值:1560818029616
在Thread-2中獲取值:1560818029616
在Thread-0中獲取值:1560818029616
複製代碼
在使用InheritableThreadLocal類須要注意的一點是:若是子線程在取得值的同時,主線程將InheritableThreadLocal中的值進行更改,那麼子線程取到的仍是舊值。
歡迎關注公衆號: