>Future 提前完成任務
首先,梳理一下,多線程爲我們帶來什麼:
- 充分利用CPU
- 當我們需要並行處理一件任務(並不一定是爲了提高運算速度,而且很多時候性能並不是絕對的問題,同一時間需要處理多個任務,就要開線程)
Future,未來,什麼是未來?
設想一種情景,線程等待生產產品的時候,想做點其他的事情
產品:
public class Product {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Product [id=" + id + ", name=" + name + "]";
}
public Product() {
}
public Product(int id, String name) {
this.id = id;
this.name = name;
new Thread(new Runnable() {
@Override
public void run() {
try {
// 模擬產品生產過程
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
}
生產工廠:
public class ProductFactory {
public Product buildProduct() {
return new Product();
}
}
測試線程:
public class Go {
public static void main(String[] args) {
ProductFactory pf=new ProductFactory();
System.out.println("準備生產產品");
Product product = pf.buildProduct();
System.out.println("生產產品的時候,我想幹點別的...");
System.out.println("產品生產完畢"+product);
}
}
結果:
準備生產產品
生產產品的時候,我想幹點別的...
產品生產完畢Product [id=1, name=tea]
上面的代碼的問題?
首先,爲了模擬產品構建比較慢,我們使用Thread.sleep(),然後我們將Thread.sleep() 又放到了一個線程中,這是一種錯誤的代碼,真正模擬生產產品,也就是初始化賦值的代碼並沒有辦法拿到線程裏面;
如果不能開線程,那麼在真正產品生產結束之前,線程真就不能幹點別的。
另外,在構造函數中不能開線程,會造成This 逃逸現象(在對象構造完成之前,就發佈了引用)。
再來,新問題:
如果現在要去蛋糕店定做一個蛋糕,蛋糕製作期間,想先處理下其他事情,先拿到訂單,做好之後再拿到實際的蛋糕
建立模型:
爲什麼要設計成這個樣子,首先找老闆要定做一個蛋糕,需要一個返回值,老闆一開始只能給我一個訂單,蛋糕和訂單 並不是同一個類型,所以要同時繼承一個接口/共同繼承一個類,這樣滿足類之間的多態。
其中訂單中對蛋糕存在一種包裝,存在依賴關係,真正幹活的還得是Real。
之前你拿蛋糕是直接在那等着做好了,直接拿蛋糕,現在有了這層 “訂單包裝”之後,既方便“老闆”先給你一個訂單,然後開線程把一個未完成的“訂單”完成,同時你去做其他事情,也可以通過“訂單”這個包裝來控制:
- 蛋糕未做完之前,來取就得等待,等做好了就通知你取“蛋糕”。
- 蛋糕做好之後來取,直接就可以獲得蛋糕。
/**
* 數據訪問接口
*/
public interface Data {
public String obtainString();
}
/**
* 類似於產品 實際數據 "蛋糕"
*/
public class Real implements Data {
public Real() {
try {
//模擬產品生產過程
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public String obtainString() {
//一旦可以調用返回,即代表產品生產完成
return "hello world";
}
}
/**
* 可以理解爲 "訂單"
*/
public class Future implements Data {
private Real real;
private boolean ready = false;
public synchronized void setReal(Real real) {
if (ready) {
return;
}
this.real = real;
this.ready = true;
notifyAll();
}
@Override
public synchronized String obtainString() {
if (!ready) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return real.obtainString();
}
}
/**
* 返回Data對象 可以理解爲 "蛋糕店"
*/
public class RequestHandler {
public Data requestData() {
final Future future = new Future();
//開了一個線程去 做"蛋糕",可以想象這個線程需要運行一段時間
new Thread(new Runnable() {
@Override
public void run() {
Real real = new Real();
future.setReal(real);
}
}).start();
//先給你一個訂單
return future;
}
}
public class Go {
public static void main(String[] args) throws InterruptedException {
RequestHandler handler = new RequestHandler();
//這裏向"老闆"handler 請求做蛋糕 ,拿到訂單
Data data1 = handler.requestData();
Data data2 = handler.requestData();
/**
* 這裏我們可以做其他事情,而不是等待
*/
System.out.println("我在做其他事情...");
// Thread.sleep(3000);
System.out.println(data1.obtainString());
System.out.println(data2.obtainString());
}
}