Threads
Using threads in Scala is similar to using threads in Java since Scala runs on the JVM and can leverage the Java concurrency utilities. Below are examples demonstrating how to create and use threads in Scala.
1. Creating and Starting a Thread
You can create a thread by extending the Thread class and overriding its run method.
class MyThread extends Thread {
override def run(): Unit = {
println(s"Thread ${Thread.currentThread().getName} is running")
}
}
// Usage
val thread1 = new MyThread()
val thread2 = new MyThread()
thread1.start()
thread2.start()
thread1.join() // Wait for thread1 to finish
thread2.join() // Wait for thread2 to finish
2. Using Runnable
Another common way is to implement the Runnable interface and pass an instance to a Thread object.
class MyRunnable extends Runnable {
override def run(): Unit = {
println(s"Runnable ${Thread.currentThread().getName} is running")
}
}
// Usage
val runnable1 = new MyRunnable()
val runnable2 = new MyRunnable()
val thread1 = new Thread(runnable1)
val thread2 = new Thread(runnable2)
thread1.start()
thread2.start()
thread1.join() // Wait for thread1 to finish
thread2.join() // Wait for thread2 to finish
3. Using Anonymous Runnable
You can also use an anonymous Runnable instance.
val thread1 = new Thread(new Runnable {
override def run(): Unit = {
println(s"Anonymous Runnable ${Thread.currentThread().getName} is running")
}
})
val thread2 = new Thread(new Runnable {
override def run(): Unit = {
println(s"Anonymous Runnable ${Thread.currentThread().getName} is running")
}
})
thread1.start()
thread2.start()
thread1.join() // Wait for thread1 to finish
thread2.join() // Wait for thread2 to finish
4. Using Lambda Expressions
In Scala, you can use lambda expressions to create Runnable instances more concisely.
val thread1 = new Thread(() => println(s"Lambda Runnable ${Thread.currentThread().getName} is running"))
val thread2 = new Thread(() => println(s"Lambda Runnable ${Thread.currentThread().getName} is running"))
thread1.start()
thread2.start()
thread1.join() // Wait for thread1 to finish
thread2.join() // Wait for thread2 to finish
5. Synchronization
To avoid race conditions, you might need to synchronize critical sections of your code.
object Counter {
private var count = 0
def increment(): Unit = synchronized {
count += 1
println(s"Count: $count by ${Thread.currentThread().getName}")
}
def getCount: Int = count
}
class IncrementThread extends Thread {
override def run(): Unit = {
for (_ <- 1 to 1000) {
Counter.increment()
}
}
}
// Usage
val threads = List.fill(10)(new IncrementThread())
threads.foreach(_.start())
threads.foreach(_.join())
println(s"Final count: ${Counter.getCount}")
6. Using Executors
For a higher-level approach, you can use the ExecutorService from the java.util.concurrent package to manage a pool of threads.
import java.util.concurrent.{Executors, ExecutorService}
val executor: ExecutorService = Executors.newFixedThreadPool(3)
val tasks = List(
new Runnable {
def run(): Unit = println(s"Task 1 running on ${Thread.currentThread().getName}")
},
new Runnable {
def run(): Unit = println(s"Task 2 running on ${Thread.currentThread().getName}")
},
new Runnable {
def run(): Unit = println(s"Task 3 running on ${Thread.currentThread().getName}")
}
)
tasks.foreach(executor.submit)
executor.shutdown() // Prevent new tasks from being submitted
executor.awaitTermination(10, java.util.concurrent.TimeUnit.SECONDS) // Wait for all tasks to complete
7. Using Future and Promise
For more advanced and functional approaches to concurrency, you can use Future and Promise in Scala.
import scala.concurrent._
import ExecutionContext.Implicits.global
import scala.util.{Success, Failure}
def longRunningTask(x: Int): Int = {
Thread.sleep(1000) // Simulate a long computation
x * x
}
val future1 = Future {
longRunningTask(10)
}
val future2 = Future {
longRunningTask(20)
}
// Combining futures
val combinedFuture = for {
result1 <- future1
result2 <- future2
} yield result1 + result2
combinedFuture.onComplete {
case Success(value) => println(s"Combined result: $value")
case Failure(exception) => println(s"Failed: $exception")
}
// Waiting for the future to complete (for demonstration purposes only, typically avoid blocking)
Await.result(combinedFuture, 5.seconds)