1.等待/通知機制
等待/通知的相關方法是任意java對象都具備的,因爲這些方法被定義在所有對象的超類java.lang.Object
- notify()
- notifyAll()
- wait(long)
- wait(long,int)
等待/通知機制是指一個線程A調用了對象O的wait()方法進入等待狀態,而另一個線程B調用了對象O的notify()或者notifyAll()方法,線程A收到通知後從對象O的wait()的方法返回,進而執行後續的操作。
package cn.smallmartial.concurrency;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Author smallmartial
* @Date 2019/8/24
* @Email [email protected]
*/
public class WaitNotify {
static boolean flag = true;
static Object lock = new Object();
public static void main(String[] args) {
Thread waitThread = new Thread(new Wait(), "WaitThread");
waitThread.start();
Thread notifyThread = new Thread(new Notify(), "NotifyThread");
notifyThread.start();
}
static class Wait implements Runnable{
@Override
public void run() {
//加鎖 擁有lock的Monitor
synchronized (lock){
//當條件不滿足時,繼續wait等待,同時釋放lock鎖
while (flag){
try {
System.out.println(Thread.currentThread()+"flag is true wait"+
new SimpleDateFormat("HH:mm:ss").format(new Date()));
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//條件滿足時,完成工作
System.out.println(Thread.currentThread()+"flag is false. running"+
new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
}
static class Notify implements Runnable{
@Override
public void run() {
//加鎖 擁有lock的montior
synchronized (lock){
//獲取lock的鎖,然後進行通知,通知時不會釋放lock的鎖
//直到當前線程釋放了lock後,waitThread才能從wait方法中返回
System.out.println(Thread.currentThread()+"hold lock。notify@1"+
new SimpleDateFormat("HH:mm:ss").format(new Date()));
lock.notifyAll();
flag = false;
SleepUtils.second(5);
}
//再次加鎖
synchronized (lock){
System.out.println(Thread.currentThread()+"hold lock again sleep@"+
new SimpleDateFormat("HH:mm:ss").format(new Date()));
SleepUtils.second(5);
}
}
}
}
等待/通知機制依託於同步機制,其目的是確保等待線程wait()返回時能夠感知到線程對變量做出的修改
等待/通知示意圖
2.等待/通知的經典範式
等待方(消費者)遵循以下原則
獲取對象的鎖
如果條件不滿足時,那麼調用對象的wait()方法,被通知仍要檢查條件
-
條件滿足則執行對應的邏輯
synchronized(對象){ while(條件不滿足時){ 對象.wait() } 對象處理的邏輯 }
通知方(生產者)遵循如下原則
獲取對象的鎖
改變條件
-
通知所有等待在對象上的線程
synchronized(對象){ 改變條件 對象。notifyAll(); }
3.Thread.join()使用
如果一個線程A執行了thread.join()的語句,其含義是:當前線程A等待thread線程終止之後才能從thread.join返回。
package cn.smallmartial.concurrency;
import java.util.concurrent.TimeUnit;
/**
* @Author smallmartial
* @Date 2019/8/24
* @Email [email protected]
*/
public class Join {
public static void main(String[] args) throws InterruptedException {
Thread previous = Thread.currentThread();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new Domino(previous), String.valueOf(i));
thread.start();
previous = thread;
}
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName()+" terminnate.");
}
static class Domino implements Runnable{
private Thread thread;
public Domino(Thread thread) {
this.thread = thread;
}
@Override
public void run() {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" terminnate.");
}
}
}
每個線程終止的前提條件是前驅線程的終止,每個線程等待前驅線程終止後,才能從join方法返回。
4.ThreadLocal使用
ThreadLocal即線程變量,是一個以ThreadLocal對象爲鍵,任意對象爲值的存儲結構。這個對象可以被附帶在線程上,也就是說一個線程可以根據一個ThreadLocal對象查詢到綁定在這個線程上的一個值。
package cn.smallmartial.concurrency;
import java.util.concurrent.TimeUnit;
/**
* @Author smallmartial
* @Date 2019/8/24
* @Email [email protected]
*/
public class Profiler {
private static final ThreadLocal<Long> TIME_THREADLOCAL = new ThreadLocal<Long>(){
protected Long initialValue(){
return System.currentTimeMillis();
}
};
public static final void begin(){
TIME_THREADLOCAL.set(System.currentTimeMillis());
}
public static final long end(){
return System.currentTimeMillis()-TIME_THREADLOCAL.get();
}
public static void main(String[] args) throws InterruptedException {
Profiler.begin();
TimeUnit.SECONDS.sleep(1);
System.out.println("Cost "+Profiler.end()+" mills");
}
}