Thinking logic of computer programs (68) and thinking 68

Source: Internet
Author: User

Thinking logic of computer programs (68) and thinking 68

This section continues with the content of the previous section and discusses how to use wait/notify to implement more collaboration scenarios.

Start at the same time

At the same time, it is similar to an athlete's competition. When we hear the game start with a gun, we start at the same time. Below, we simulate this process. Here there is a main thread and N subthreads, each sub-thread simulates an athlete and the main thread simulates the referee. The shared variable of their collaboration is a starting signal. We use a FireFlag class to represent this collaboration object. The Code is as follows:

static class FireFlag {    private volatile boolean fired = false;    public synchronized void waitForFire() throws InterruptedException {        while (!fired) {            wait();        }    }    public synchronized void fire() {        this.fired = true;        notifyAll();    }}

The sub-thread should call waitForFire () to wait for the gun to ring, and the main thread should call fire () to start the game.

The following table lists the types of contestants:

static class Racer extends Thread {    FireFlag fireFlag;    public Racer(FireFlag fireFlag) {        this.fireFlag = fireFlag;    }    @Override    public void run() {        try {            this.fireFlag.waitForFire();            System.out.println("start run "                    + Thread.currentThread().getName());        } catch (InterruptedException e) {        }    }}

The main program code is as follows:

public static void main(String[] args) throws InterruptedException {    int num = 10;    FireFlag fireFlag = new FireFlag();    Thread[] racers = new Thread[num];    for (int i = 0; i < num; i++) {        racers[i] = new Racer(fireFlag);        racers[i].start();    }    Thread.sleep(1000);    fireFlag.fire();}

Here, 10 sub-threads are started. After each sub-thread starts, it waits for the fire signal. After the main thread calls fire (), each sub-thread starts to execute subsequent operations.

Wait for end

Understanding join

In understanding Synchronized, we use the join method to let the main thread wait for the subthread to end. The join actually calls wait. The main code is:

while (isAlive()) {    wait(0);}

As long as the thread is alive and isAlive () returns true, join continues to wait. Who will notify it? When the thread stops running, the Java System calls yyall to notify you.

Use collaboration objects

It is sometimes troublesome to use join. The main thread needs to wait for each subthread one by one. Here, we will demonstrate a new method. The shared variable for collaboration between the main thread and each sub-thread is a number, which indicates the number of unfinished threads. The initial value is the number of sub-threads. The main thread waits for this value to change to 0, after each sub-thread ends, this value is reduced by one. When it is reduced to 0, notifyAll is called. We use MyLatch to represent this collaboration object. The sample code is as follows:

public class MyLatch {    private int count;    public MyLatch(int count) {        this.count = count;    }    public synchronized void await() throws InterruptedException {        while (count > 0) {            wait();        }    }    public synchronized void countDown() {        count--;        if (count <= 0) {            notifyAll();        }    }}

Here, the MyLatch constructor parameter count should be initialized to the number of sub-threads, the main thread should call await (), and the Sub-thread should call countDown () after execution ().

The sample code of the worker subthread is as follows:

static class Worker extends Thread {    MyLatch latch;    public Worker(MyLatch latch) {        this.latch = latch;    }    @Override    public void run() {        try {            // simulate working on task            Thread.sleep((int) (Math.random() * 1000));            this.latch.countDown();        } catch (InterruptedException e) {        }    }}

The sample code of the main thread is as follows:

public static void main(String[] args) throws InterruptedException {    int workerNum = 100;    MyLatch latch = new MyLatch(workerNum);    Worker[] workers = new Worker[workerNum];    for (int i = 0; i < workerNum; i++) {        workers[i] = new Worker(latch);        workers[i].start();    }    latch.await();    System.out.println("collect worker results");}

MyLatch is a tool class for Synchronous collaboration. It is mainly used to demonstrate basic principles. There is a special synchronization class CountDownLatch in Java, which should be used in actual development, we will introduce it in subsequent chapters.

The functions of MyLatch are quite common. They can also be used in the above "start at the same time" scenario. The initial value is set to 1. The Racer class calls await () and the main thread calls countDown, as follows:

public class RacerWithLatchDemo {    static class Racer extends Thread {        MyLatch latch;        public Racer(MyLatch latch) {            this.latch = latch;        }        @Override        public void run() {            try {                this.latch.await();                System.out.println("start run "                        + Thread.currentThread().getName());            } catch (InterruptedException e) {            }        }    }    public static void main(String[] args) throws InterruptedException {        int num = 10;        MyLatch latch = new MyLatch(1);        Thread[] racers = new Thread[num];        for (int i = 0; i < num; i++) {            racers[i] = new Racer(latch);            racers[i].start();        }        Thread.sleep(1000);        latch.countDown();    }}

Asynchronous result

In the master-slave mode, manual thread creation is often troublesome. A common mode is asynchronous call. An asynchronous call returns an object generally called Promise or Future, you can use it to obtain the final result. In Java, the subtask interface is Callable and declared:

public interface Callable<V> {    V call() throws Exception;}

To indicate the asynchronous call result, we define an interface MyFuture, as shown below:

public interface MyFuture <V> {    V get() throws Exception ;}

The get method of this interface returns the real result. If the result has not been computed, the get method will be blocked until the calculation is complete. If an exception occurs during the call, the get method throws an exception during the call.

To facilitate the main thread to call subtasks, we define a class MyExecutor, which defines a public method execute to execute subtasks and return asynchronous results. The declaration is as follows:

public <V> MyFuture<V> execute(final Callable<V> task)

With this method, for the main thread, it does not need to create and manage subthreads, and can easily obtain the results of asynchronous calls, for example, in the main thread, you can start asynchronous call and obtain the result as follows:

Public static void main (String [] args) {MyExecutor executor = new MyExecutor (); // subTask Callable <Integer> subTask = new Callable <Integer> () {@ Override public Integer call () throws Exception {//... run the asynchronous task int millis = (int) (Math. random () * 1000); Thread. sleep (millis); return millis ;}}; // asynchronous call, returns a MyFuture object MyFuture <Integer> future = executor.exe cute (subTask );//... execute other operations try {// obtain the asynchronous call result Integer result = future. get (); System. out. println (result);} catch (Exception e) {e. printStackTrace ();}}

How is the execute method of MyExecutor implemented? It encapsulates the process of creating a subthread and synchronously obtaining results. It creates an execution subthread. The code of this subthread is as follows:

static class ExecuteThread<V> extends Thread {    private V result = null;    private Exception exception = null;    private boolean done = false;    private Callable<V> task;    private Object lock;        public ExecuteThread(Callable<V> task, Object lock) {        this.task = task;        this.lock = lock;    }    @Override    public void run() {        try {            result = task.call();        } catch (Exception e) {            exception = e;        } finally {            synchronized (lock) {                done = true;                lock.notifyAll();            }        }    }    public V getResult() {        return result;    }    public boolean isDone() {        return done;    }    public Exception getException() {        return exception;    }}

This sub-thread executes the actual sub-task, records the execution result to the result variable, and exception to the exception variable, after the execution is complete, set the sharing status variable done to true and call policyall to wake up the main thread that may be waiting for the results.

The code of the execute method of MyExecutor is:

public <V> MyFuture<V> execute(final Callable<V> task) {    final Object lock = new Object();    final ExecuteThread<V> thread = new ExecuteThread<>(task, lock);    thread.start();    MyFuture<V> future = new MyFuture<V>() {        @Override        public V get() throws Exception {            synchronized (lock) {                while (!thread.isDone()) {                    try {                        lock.wait();                    } catch (InterruptedException e) {                    }                }                if (thread.getException() != null) {                    throw thread.getException();                }                return thread.getResult();            }        }    };    return future;}

Execute starts a thread and returns the MyFuture object. The get method of MyFuture will block and wait until the thread stops running.

The above MyExecutore and MyFuture are mainly used to demonstrate the basic principles. In fact, Java already contains a complete framework called Executors. Some related interfaces and classes include:

  • Indicates the asynchronous result interface Future and implementation class FutureTask
  • Executor for executing asynchronous tasks and ExecutorService for more sub-interfaces
  • The factory method class Executors used to create Executor and ExecutorService

We will introduce this framework in detail in subsequent chapters.

Meeting Point

Each thread first performs a split-head operation and then arrives at a set point. At the set point, all threads need to be collected, data is exchanged, and then the next action is performed. How does one represent such collaboration? The shared variable of collaboration is still a number. This number indicates the number of threads that have not reached the set point. The initial value is the number of sub-threads. After each thread reaches the set point, this value is reduced by one. If it is not 0, wait until another thread arrives. If it changes to 0, it means that it is the last thread. Call yyall to wake up all threads. We use the AssemblePoint class to represent this collaboration object. The sample code is as follows:

public class AssemblePoint {    private int n;    public AssemblePoint(int n) {        this.n = n;    }    public synchronized void await() throws InterruptedException {        if (n > 0) {            n--;            if (n == 0) {                notifyAll();            } else {                while (n != 0) {                    wait();                }            }        }    }}

Multiple tourist threads run independently, and then use the collaboration object to synchronize at the collection point. The sample code is as follows:

Public class AssemblePointDemo {static class Tourist extends Thread {AssemblePoint ap; public Tourist (AssemblePoint ap) {this. ap = ap ;}@ Override public void run () {try {// simulate independent Thread operation. sleep (int) (Math. random () * 1000); // set ap. await (); System. out. println ("arrived ");//... execute other operations after collection} catch (InterruptedException e) {}} public static void main (String [] args) {int num = 10; tourist [] threads = new Tourist [num]; AssemblePoint ap = new AssemblePoint (num); for (int I = 0; I <num; I ++) {threads [I] = new Tourist (ap); threads [I]. start ();}}}

The implementation of AssemblePoint is mainly used to demonstrate the basic principle. Java has a special synchronization tool class named CyclicBarrier to replace it. For this class, we will introduce it in subsequent chapters.

Summary

This section describes the basic mechanism of thread-to-thread collaboration in Java, wait/notify. The key to collaboration is to understand the shared variables and conditions of collaboration. For further understanding, it is applicable to multiple collaboration scenarios, we demonstrated the usage and basic collaboration principles of wait/notify. Java provides blocking queues, synchronization tools, and the Executors framework for collaboration. We will introduce them in subsequent chapters, in actual development, we should try to use these ready-made classes instead of re-inventing the wheel.

Previously, we encountered InterruptedException multiple times and chose to ignore it. Now it is time to learn more about it.

(As in other sections, all the code in this section is in the https://github.com/swiftma/program-logic)

----------------

For more information, see the latest article. Please pay attention to the Public Account "lauma says programming" (scan the QR code below), from entry to advanced, ma and you explore the essence of Java programming and computer technology. Retain All copyrights with original intent.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.