We know that multithreading allows you to perform multiple tasks at the same time (it just looks like it’s happening at the same time, but it turns out that the CPU time slice is switching so fast that we don’t notice it).
Now imagine a cooking scenario where you have no utensils and no ingredients. You can go online and buy a cookware, but during this time, you don’t have to be idle, you can also go to the supermarket to buy ingredients.
Imagine two threads, the main thread to buy ingredients, and then a child thread to buy kitchenware. However, the child thread needs to return a cooker. What if, using a normal thread, there is only one Run method and the Run method returns no value?
We can use the Future schema provided by the JDK. After the main thread buys the ingredients, it can actively obtain the kitchen utensils of the child thread. (This article assumes that the reader knows Future, so we won’t say much about Future usage.)
The code is as follows:
public class FutureCook { static class Chuju { } static class Shicai{ } public static void cook(Chuju chuju,Shicai Shicai){system.out.println (" Last: Cooking..." ); } public static void main(String[] args) throws InterruptedException, ExecutionException {// first step, call <Chuju> shopping = new call <Chuju>(){@override public Chuju Call () throws Exception {system.out.println (" first step: order "); System.out.println(" first step: wait for delivery "); Thread.sleep(5000); System.out.println(" first step: express to "); return new Chuju(); }}; FutureTask<Chuju> task = new FutureTask<Chuju>(shopping); new Thread(task).start(); Thread.sleep(2000); Shicai shicai = new Shicai(); System.out.println(" Step 2: Ingredients in place "); // Step 3, cook if(! Task.isdone ()){system.out.println (" Step 3: kitchen not arrived, please wait, can also cancel "); / / 1 / / task. Cancel (true); // system.out.println (" canceled "); // system.out.println (" canceled "); // return; } Chuju Chuju = task.get(); System.out.println(" Step 3: utensils in place, ready to cook "); cook(chuju,shicai); }}Copy the code
Return result:
Step 1: Order Step 1: Waiting for delivery Step 2: Ingredients in place Step 3: Kitchenware has not arrived, please wait, or cancel Step 1: Courier delivery Step 3: Kitchenware in place, ready to cook Finally: Cooking...Copy the code
The code above shows that the sub-thread takes a long time to buy utensils (say 5 seconds) and the main thread takes a fast time to buy ingredients (2 seconds), so I check to see if the kitchenware buying thread is finished before I start cooking in step 3. It must return false, and the main thread can choose to continue waiting or cancel. (Open the comment to cancel the test)
We can see that the Future mode can be used to change the synchronous execution of tasks to asynchronous execution, which can make full use of CPU resources and improve efficiency.
Now, I use wait and notify to achieve the same effect as the Future mode above.
The idea is to create a FutureClient end to initiate the request, use FutureData to immediately return a result (equivalent to only returning a successful request notification), and then start a thread to execute the task asynchronously to obtain the RealData RealData. At this point, the main thread can continue to perform other tasks, and when it needs data, it can call the get method to get the real data.
1) Define a data interface, including get method to get data, isDone method to judge whether the task is completed, and cancel method to cancel the task.
public interface Data<T> {
T get();
boolean isDone();
boolean cancel();
}Copy the code
2) Define real Data class, implement Data interface, used to perform actual tasks and return real Data.
public class RealData<T> implements Data<T>{ private T result ; public RealData (){ this.prepare(); } private void prepare() {try {system.out.println (" first step: order "); System.out.println(" first step: wait for delivery "); Thread.sleep(5000); System.out.println(" first step: express to "); } catch (InterruptedException e) {system.out.println (" interrupted :"+e); // Reset the interrupt state thread.currentThread ().interrupt(); } Main.Chuju chuju = new Main.Chuju(); result = (T)chuju; } @Override public T get() { return result; } @Override public boolean isDone() { return false; } @Override public boolean cancel() { return true; }}Copy the code
The prepare method is used to prepare data, which is the actual task to perform. The get method is used to return the execution result of the task.
3) Define a proxy class FutureData to temporarily return dummy data to the requester FutureClient. Load the real data after you get the real data.
public class FutureData<T> implements Data<T>{ private RealData<T> realData ; private boolean isReady = false; private Thread runningThread; Public synchronized void setRealData(RealData){// If (isReady){return; } // If not, load the real object this.realData = realData; isReady = true; // notify notify(); } @override public synchronized T get() {Override public synchronized T get() { isReady){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); Return realdata.get (); } public boolean isDone() { return isReady; } @Override public boolean cancel() { if(isReady){ return false; } runningThread.interrupt(); return true; } public void setRunningThread(){ runningThread = Thread.currentThread(); }}Copy the code
If get is called, it checks whether the data has been loaded (isReady), and if not, it calls wait.
SetRealData is used to load real data, isReady is set to true after loading, and notify the waiting thread by calling notify. At this point, the get method is notified and continues to execute, returning realData realdata.get ().
In addition, it simply implements a task cancellation method called Cancel to interrupt the thread that is executing the subtask.
4) FutureClient client is used to initiate requests and perform tasks asynchronously.
Public class FutureClient {public Data Call (){// Create a proxy object FutureData, Final FutureData FutureData = new FutureData(); New Thread(new Runnable() {@override public void run() {public void run() {public void run() { futureData.setRunningThread(); RealData realData = new RealData(); // Assign the result to the proxy object futureData.setrealData (realData); } }).start(); return futureData; }}Copy the code
5) test
public class Main { static class Chuju{ } static class Shicai{ } public static void main(String[] args) throws InterruptedException { FutureClient fc = new FutureClient(); Data data = fc.call(); Thread.sleep(2000); Shicai shicai = new Shicai(); System.out.println(" Step 2: Ingredients in place "); if(! Data.isdone ()){system.out.println (" Step 3: the kitchen has not arrived, please wait or cancel "); / / (2) / / data. The cancel (); // system.out.println (" canceled "); // system.out.println (" canceled "); // return; Chuju Chuju = (Chuju)data.get(); System.out.println(" Step 3: utensils in place, ready to cook "); cook(chuju,shicai); } public static void cook (Chuju Chuju, Shicai Shicai){system.out.println (" ); }}Copy the code
The result is exactly the same as the Future schema provided with the JDK. We can also open the code from ② to test the result of task cancellation.
The first step: the first step in order: wait for the delivery Step 2: ingredients in place Step 3: kitchen utensils and appliances hasn't arrived, please wait or cancel cancelled interrupted: Java. Lang. InterruptedException: sleep interruptedCopy the code
After the cancellation, the child thread executing RealData is interrupted and the task terminates.