Search code examples
javamultithreadingscalaakkaactor

From Java multithreading to Scala Akka actors


I'm new to Scala, so i need a hand. I have an example done with Java and i must create the same thing using Scala. The example is this:

import java.util.ArrayList;

public class Z 
{
    public static void main(String[] args) 
    {
        A a=new A();
        B b=new B(a);
        a.setB(b);
        a.addMessage("A1");
        a.addMessage("A2");
        a.addMessage("A3");
        b.addMessage("B1");
        b.addMessage("B2");
        b.addMessage("B3");
        b.addMessage("B4");
        a.start();
        b.start();
    }   
}

class A extends Thread
{
    ArrayList<String> msgs=new ArrayList<String>();
    B b;

    public void setB(B tb)
    {
        b=tb;
    }

    public void run() //It's like a send() method
    {
        for (int i=0; i<msgs.size(); i++)
        {
            final String msg=msgs.get(i);
            new Thread()
            {
                public void run() 
                {
                    b.receive(msg);
                }
            }.start();
        }
    }

    public void receive (String msg)
    {
        System.out.println("A received a message from B.");
    }

    public void addMessage(String message)
    {
        msgs.add(message);
    }
}

class B extends Thread
{
    ArrayList<String> msgs=new ArrayList<String>();
    A a;

    public B(A ta)
    {
        a=ta;
    }

    public void run() //It's like a send() method
    {
        for (int i=0; i<msgs.size(); i++)
        {
            final String msg=msgs.get(i);
            new Thread()
            {
                public void run() 
                {
                    a.receive(msg);
                }
            }.start();
        }
    }

    public void receive (String msg)
    {
        System.out.println("B received a message from A.");
    }

    public void addMessage(String message)
    {
        msgs.add(message);
    }
}

The creation of the two actor is similar, but the problem is with the run(). I know i can use something like this

receive
{
    case msg=>println("A received a message from B."); 
}

in the two act() to receive the messages (and i must say that is very usefull), but i can't figure out how to recreate the parts that send the messages. Should i start the actors in the main and then send the messages outside the act()?


Solution

  • Consider the following implementation with Akka actors. Note that

    • the run method is here replaced with a SendAll message;
    • the actor reference passing between actors may be simplified by adding such reference to SendAll;
    • a minor improvement toward code succinctness is the encapsulation of each text message onto a list;
    • an actor logging system replaces println.

    Hence,

    import akka.actor.{ActorSystem, Props, Actor, ActorRef, ActorLogging}
    import collection.mutable.ListBuffer
    
    sealed trait Messaging
    case class Add(msgs: List[String]) extends Messaging
    case class Receive(msgs: String) extends Messaging
    case class SendAll(dest: ActorRef) extends Messaging 
    
    
    class Messenger extends Actor with ActorLogging {
      var messages = new ListBuffer[String]()
    
      def receive = {
        case Add(msgs)     => messages ++= msgs
        case Receive(msg)  => log.info(s"received $msg from ${sender.path}.")
        case SendAll(dest) => messages.foreach(dest ! Receive(_))
      }
    }
    
    object Main extends App {
    
      val system = ActorSystem("MessengerSystem")
      val aActor = system.actorOf(Props[Messenger], name = "aActor")
      val bActor = system.actorOf(Props[Messenger], name = "bActor")
    
      aActor ! Add(List("A1","A2","A3"))
      bActor ! Add(List("B1","B2","B3","B4"))
    
      aActor ! SendAll(bActor)
      bActor ! SendAll(aActor)
    
      system.shutdown
    }