La concurrencia es un concepto fundamental en la programación moderna, especialmente en aplicaciones que requieren alta disponibilidad y rendimiento. Scala proporciona varias herramientas y bibliotecas para manejar la concurrencia de manera eficiente. En esta sección, exploraremos los conceptos básicos de la concurrencia en Scala, las herramientas disponibles y cómo utilizarlas.

Contenidos

Conceptos Básicos de Concurrencia

La concurrencia se refiere a la capacidad de un programa para ejecutar múltiples tareas de manera simultánea. En Scala, la concurrencia se puede manejar de varias maneras:

  • Hilos (Threads): La forma más básica de concurrencia, donde cada tarea se ejecuta en un hilo separado.
  • Futuros y Promesas: Abstracciones de alto nivel para manejar tareas asincrónicas.
  • Actores: Un modelo de concurrencia basado en el paso de mensajes, implementado en la biblioteca Akka.
  • Colecciones Paralelas: Permiten realizar operaciones en colecciones de manera concurrente.

Ejemplo Básico de Hilos

object ThreadExample extends App {
  val thread1 = new Thread(new Runnable {
    def run(): Unit = {
      println("Thread 1 is running")
    }
  })

  val thread2 = new Thread(new Runnable {
    def run(): Unit = {
      println("Thread 2 is running")
    }
  })

  thread1.start()
  thread2.start()
}

En este ejemplo, creamos dos hilos que imprimen mensajes diferentes. Cada hilo se ejecuta de manera concurrente.

Futuros y Promesas

Los futuros y las promesas son abstracciones de alto nivel para manejar tareas asincrónicas. Un Future representa un valor que puede estar disponible en algún momento en el futuro, mientras que una Promise es un contenedor que puede completar un Future.

Ejemplo de Futuros

import scala.concurrent.{Future, ExecutionContext}
import scala.util.{Success, Failure}

object FutureExample extends App {
  implicit val ec: ExecutionContext = ExecutionContext.global

  val future = Future {
    // Simulando una tarea que toma tiempo
    Thread.sleep(1000)
    42
  }

  future.onComplete {
    case Success(value) => println(s"El resultado es $value")
    case Failure(e) => println(s"Error: ${e.getMessage}")
  }

  // Mantener la aplicación viva para ver el resultado
  Thread.sleep(2000)
}

En este ejemplo, creamos un Future que simula una tarea que toma tiempo (1 segundo). Usamos onComplete para manejar el resultado del Future.

Actores y Akka

Akka es una biblioteca poderosa para manejar la concurrencia en Scala usando el modelo de actores. Un actor es una entidad que puede recibir y procesar mensajes.

Ejemplo de Actores con Akka

import akka.actor.{Actor, ActorSystem, Props}

class SimpleActor extends Actor {
  def receive: Receive = {
    case msg: String => println(s"Received message: $msg")
  }
}

object AkkaExample extends App {
  val system = ActorSystem("SimpleSystem")
  val simpleActor = system.actorOf(Props[SimpleActor], "simpleActor")

  simpleActor ! "Hello, Actor"
  system.terminate()
}

En este ejemplo, creamos un actor simple que imprime los mensajes que recibe. Usamos ActorSystem para crear y gestionar el ciclo de vida del actor.

Paralelismo y Colecciones Paralelas

Scala proporciona colecciones paralelas que permiten realizar operaciones en colecciones de manera concurrente.

Ejemplo de Colecciones Paralelas

object ParallelCollectionsExample extends App {
  val list = (1 to 1000000).toList

  val sum = list.par.sum
  println(s"Sum: $sum")
}

En este ejemplo, usamos la colección paralela par para sumar los elementos de una lista de manera concurrente.

Ejercicios Prácticos

Ejercicio 1: Uso de Futuros

Instrucciones:

  1. Crea un Future que simule una tarea que toma 2 segundos y devuelva el valor 100.
  2. Usa onComplete para imprimir el resultado del Future.

Solución:

import scala.concurrent.{Future, ExecutionContext}
import scala.util.{Success, Failure}

object FutureExercise extends App {
  implicit val ec: ExecutionContext = ExecutionContext.global

  val future = Future {
    Thread.sleep(2000)
    100
  }

  future.onComplete {
    case Success(value) => println(s"El resultado es $value")
    case Failure(e) => println(s"Error: ${e.getMessage}")
  }

  Thread.sleep(3000)
}

Ejercicio 2: Uso de Actores

Instrucciones:

  1. Crea un actor que reciba un mensaje de tipo Int y calcule su cuadrado.
  2. Envía un mensaje con el valor 5 al actor y verifica que el resultado sea 25.

Solución:

import akka.actor.{Actor, ActorSystem, Props}

class SquareActor extends Actor {
  def receive: Receive = {
    case num: Int => println(s"El cuadrado de $num es ${num * num}")
  }
}

object ActorExercise extends App {
  val system = ActorSystem("SquareSystem")
  val squareActor = system.actorOf(Props[SquareActor], "squareActor")

  squareActor ! 5
  system.terminate()
}

Conclusión

En esta sección, hemos explorado los conceptos básicos de la concurrencia en Scala, incluyendo hilos, futuros, promesas, actores y colecciones paralelas. La concurrencia es una herramienta poderosa que puede mejorar significativamente el rendimiento y la capacidad de respuesta de tus aplicaciones. A medida que avances en tu aprendizaje de Scala, te encontrarás utilizando estas herramientas de manera más frecuente y sofisticada.

En la próxima sección, profundizaremos en el ecosistema y las herramientas de Scala, comenzando con SBT, la herramienta de construcción de Scala.

© Copyright 2024. Todos los derechos reservados