Improved java version of pi example also. See #1729
This commit is contained in:
parent
c64b73004a
commit
fa41cea897
5 changed files with 239 additions and 200 deletions
|
|
@ -268,11 +268,16 @@ We start by creating case classes for each type of message in our application, s
|
|||
call it ``PiMessage``. Right click on the package and choose ``New Scala Class``, and enter ``PiMessage`` as
|
||||
the name of the class.
|
||||
|
||||
We need three different messages:
|
||||
We need four different messages:
|
||||
|
||||
- ``Calculate`` -- sent to the ``Master`` actor to start the calculation
|
||||
- ``Work`` -- sent from the ``Master`` actor to the ``Worker`` actors containing the work assignment
|
||||
- ``Result`` -- sent from the ``Worker`` actors to the ``Master`` actor containing the result from the worker's calculation
|
||||
- ``Work`` -- sent from the ``Master`` actor to the ``Worker`` actors containing
|
||||
the work assignment
|
||||
- ``Result`` -- sent from the ``Worker`` actors to the ``Master`` actor
|
||||
containing the result from the worker's calculation
|
||||
- ``PiApproximation`` -- sent from the ``Master`` actor to the
|
||||
``Listener`` actor containing the the final pi result and how long time
|
||||
the calculation took
|
||||
|
||||
Messages sent to actors should always be immutable to avoid sharing mutable state.
|
||||
In Scala we have 'case classes' which make excellent messages. So let's start by creating three messages as case classes.
|
||||
|
|
@ -343,19 +348,8 @@ Here is the master actor:
|
|||
|
||||
A couple of things are worth explaining further.
|
||||
|
||||
First, we are passing in a ``java.util.concurrent.CountDownLatch`` to the
|
||||
``Master`` actor. This latch is only used for plumbing (in this specific
|
||||
tutorial), to have a simple way of letting the outside world knowing when the
|
||||
master can deliver the result and shut down. In more idiomatic Akka code
|
||||
we would not use a latch but other abstractions and functions like ``Future``
|
||||
and ``?`` to achieve the same thing in a non-blocking way.
|
||||
But for simplicity let's stick to a ``CountDownLatch`` for now.
|
||||
|
||||
Second, we are adding a couple of life-cycle callback methods; ``preStart`` and
|
||||
``postStop``. In the ``preStart`` callback we are recording the time when the
|
||||
actor is started and in the ``postStop`` callback we are printing out the result
|
||||
(the approximation of Pi) and the time it took to calculate it. In this call we
|
||||
also invoke ``latch.countDown()`` to tell the outside world that we are done.
|
||||
Note that we are passing in a ``ActorRef`` to the ``Master`` actor. This is used to
|
||||
report the the final result to the outside world.
|
||||
|
||||
But we are not done yet. We are missing the message handler for the ``Master``
|
||||
actor. This message handler needs to be able to react to two different messages:
|
||||
|
|
@ -368,7 +362,8 @@ The ``Calculate`` handler is sending out work to all the ``Worker`` via its rout
|
|||
The ``Result`` handler gets the value from the ``Result`` message and aggregates it to
|
||||
our ``pi`` member variable. We also keep track of how many results we have received back,
|
||||
and if that matches the number of tasks sent out, the ``Master`` actor considers itself done and
|
||||
invokes the ``self.stop()`` method to stop itself *and* all its supervised actors.
|
||||
sends the final result to the ``listener``. When done it also invokes the ``context.stop(self)``
|
||||
method to stop itself *and* all its supervised actors.
|
||||
In this case it has one supervised actor, the router, and this in turn has ``nrOfWorkers`` supervised actors.
|
||||
All of them will be stopped automatically as the invocation of any supervisor's ``stop`` method
|
||||
will propagate down to all its supervised 'children'.
|
||||
|
|
@ -377,6 +372,13 @@ Let's capture this in code:
|
|||
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#master-receive
|
||||
|
||||
Creating the result listener
|
||||
----------------------------
|
||||
|
||||
The listener is straightforward. When it receives the ``PiApproximation`` from the ``Master`` it
|
||||
prints the result and shuts down the ``ActorSystem``.
|
||||
|
||||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#result-listener
|
||||
|
||||
Bootstrap the calculation
|
||||
-------------------------
|
||||
|
|
@ -391,6 +393,12 @@ We also create a method ``calculate`` in which we start up the ``Master`` actor
|
|||
.. includecode:: ../../akka-tutorials/akka-tutorial-first/src/main/scala/Pi.scala#app
|
||||
:exclude: actors-and-messages
|
||||
|
||||
As you can see the *calculate* method above it creates an ``ActorSystem`` and this is the Akka container which
|
||||
will contain all actors created in that "context". An example of how to create actors in the container
|
||||
is the *'system.actorOf(...)'* line in the calculate method. In this case we create two top level actors.
|
||||
If you instead where in an actor context, i.e. inside an actor creating other actors, you should use
|
||||
*context.actorOf(...)*. This is illustrated in the Master code above.
|
||||
|
||||
That's it. Now we are done.
|
||||
|
||||
Run it from Eclipse
|
||||
|
|
@ -401,8 +409,8 @@ If not, bring you project up to date by clicking ``Project/Build Project``. If t
|
|||
you can right-click in the editor where ``Pi`` is defined, and choose ``Run as.. /Scala application``.
|
||||
If everything works fine, you should see::
|
||||
|
||||
Pi estimate: 3.1435501812459323
|
||||
Calculation time: 632 millis
|
||||
Pi approximation: 3.1435501812459323
|
||||
Calculation time: 359 millis
|
||||
|
||||
You can also define a new Run configuration, by going to ``Run/Run Configurations``. Create a new ``Scala application``
|
||||
and choose the tutorial project and the main class to be ``akkatutorial.Pi``. You can pass additional command line
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue