How do I run coroutines in parallel using async and await in Kotlin?

In Kotlin, use async to start concurrent work inside a coroutine scope, then call await() to get each result.

Basic example:

import kotlinx.coroutines.*

suspend fun fetchUser(): String {
    delay(1000)
    return "User"
}

suspend fun fetchPosts(): List<String> {
    delay(1000)
    return listOf("Post 1", "Post 2")
}

fun main() = runBlocking {
    val userDeferred = async {
        fetchUser()
    }

    val postsDeferred = async {
        fetchPosts()
    }

    val user = userDeferred.await()
    val posts = postsDeferred.await()

    println(user)
    println(posts)
}

Here, fetchUser() and fetchPosts() run in parallel. Since both delay for 1 second, the total time is about 1 second instead of 2.

You can also await multiple results with awaitAll:

import kotlinx.coroutines.*

fun main() = runBlocking {
    val deferredResults = listOf(
        async {
            delay(1000)
            "Result 1"
        },
        async {
            delay(1000)
            "Result 2"
        },
        async {
            delay(1000)
            "Result 3"
        }
    )

    val results = deferredResults.awaitAll()

    println(results)
}

Output:

[Result 1, Result 2, Result 3]

A common pattern is:

coroutineScope {
    val a = async { doWorkA() }
    val b = async { doWorkB() }

    val resultA = a.await()
    val resultB = b.await()

    combine(resultA, resultB)
}

For example:

import kotlinx.coroutines.*

suspend fun loadName(): String {
    delay(500)
    return "Alice"
}

suspend fun loadAge(): Int {
    delay(500)
    return 30
}

suspend fun loadProfile(): String = coroutineScope {
    val name = async { loadName() }
    val age = async { loadAge() }

    "${name.await()} is ${age.await()} years old"
}

fun main() = runBlocking {
    println(loadProfile())
}

Important notes:

  • async returns a Deferred<T>.
  • await() suspends until the result is ready.
  • async should usually be called inside coroutineScope, supervisorScope, runBlocking, or another coroutine.
  • If one async child fails inside coroutineScope, the whole scope is cancelled.
  • Use async for concurrent computations that return values.
  • Use launch for concurrent work that does not return a value.

If you are doing blocking I/O, use an appropriate dispatcher:

val data = async(Dispatchers.IO) {
    blockingNetworkCall()
}

For CPU-heavy work:

val result = async(Dispatchers.Default) {
    heavyCalculation()
}

How do I write a suspend function in Kotlin?

In Kotlin, a suspend function is declared with the suspend modifier.

suspend fun fetchUser(): User {
    // Can call other suspend functions here
    return api.getUser()
}

A suspend function can pause without blocking the thread and later resume. It is commonly used with coroutines for asynchronous work.

Example:

import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

suspend fun greetAfterDelay() {
    delay(1000)
    println("Hello after 1 second")
}

fun main() = runBlocking {
    greetAfterDelay()
}

Key points:

  • Use suspend fun to define one.
  • A suspend function can call other suspend functions.
  • It must be called from:
    • another suspend function, or
    • a coroutine scope such as runBlocking, launch, or async.

Example with return value:

suspend fun loadMessage(): String {
    delay(500)
    return "Loaded message"
}

fun main() = runBlocking {
    val message = loadMessage()
    println(message)
}

So the basic syntax is:

suspend fun functionName() {
    // suspending work
}

How do I use delay instead of Thread.sleep in Kotlin coroutines?

Use kotlinx.coroutines.delay(...) inside a coroutine instead of Thread.sleep(...).

Thread.sleep blocks the current thread. delay suspends the coroutine without blocking the thread, so other coroutines can keep running.

import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    println("Before delay")

    delay(1_000) // suspends for 1 second, does not block the thread

    println("After delay")
}

If you currently have:

Thread.sleep(1000)

replace it with:

delay(1000)

But note: delay is a suspend function, so it can only be called from another suspend function or from inside a coroutine builder such as launch, async, or runBlocking.

Example with launch:

import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    launch {
        delay(1000)
        println("Coroutine finished after 1 second")
    }

    println("This prints immediately")
}

Example in a suspend function:

import kotlinx.coroutines.delay

suspend fun doWork() {
    delay(500)
    println("Work done")
}

Avoid doing this in coroutines:

Thread.sleep(1000) // blocks the underlying thread

Prefer:

delay(1000) // suspends only the coroutine

How do I launch a coroutine in Kotlin using launch and runBlocking?

In Kotlin, you can use runBlocking to start a coroutine scope that blocks the current thread until its coroutines finish, and launch to start a new coroutine inside that scope.

import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    launch {
        println("Coroutine is running")
    }

    println("Main coroutine continues")
}

Output may look like:

Main coroutine continues
Coroutine is running

runBlocking creates a coroutine and blocks the current thread until all child coroutines complete.

launch starts a new coroutine that runs concurrently with the rest of the code inside the runBlocking scope.

A slightly clearer example:

import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    launch {
        delay(1000)
        println("World!")
    }

    println("Hello")
}

Output:

Hello
World!

Here:

  • runBlocking { ... } starts a blocking coroutine scope.
  • launch { ... } starts a child coroutine.
  • delay(1000) suspends the coroutine for 1 second without blocking the thread.
  • runBlocking waits until the launched coroutine finishes before main exits.

How do I use external configuration with Spring?

External configuration means keeping settings such as application names, URLs, ports, feature flags, credentials, or environment-specific values outside your Java code.

Spring supports this mainly through:

  • application.properties
  • application.yml
  • environment variables
  • command-line arguments
  • external property files
  • @Value
  • @ConfigurationProperties

1. Using application.properties

In a Spring or Spring Boot application, you can place configuration in:

src/main/resources/application.properties

Example:

app.name=My Spring App
app.version=1.0.0
app.description=Example application using external configuration

Then inject values with @Value:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class AppProperties {

    @Value("${app.name}")
    private String appName;

    @Value("${app.version}")
    private String appVersion;

    @Value("${app.description}")
    private String appDescription;

    public void printProperties() {
        System.out.println("App Name: " + appName);
        System.out.println("App Version: " + appVersion);
        System.out.println("App Description: " + appDescription);
    }
}

2. Using application.yml

You can also use YAML:

app:
  name: My Spring App
  version: 1.0.0
  description: Example application using external configuration

The same @Value expressions still work:

@Value("${app.name}")
private String appName;

3. Using @PropertySource in non-Boot Spring

If you are using plain Spring with Java configuration, register a property file using @PropertySource:

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@ComponentScan("com.example.app")
@PropertySource("classpath:application.properties")
public class AppConfig {
}

Then Spring can resolve values like:

@Value("${app.name}")
private String appName;

If you are using Spring Boot, you usually do not need @PropertySource for application.properties or application.yml. Spring Boot loads them automatically.


4. Recommended Spring Boot Approach: @ConfigurationProperties

For multiple related properties, prefer @ConfigurationProperties over many @Value fields.

Example application.properties:

app.name=My Spring App
app.version=1.0.0
app.description=Example application using external configuration

Create a properties class:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {

    private String name;
    private String version;
    private String description;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

Then inject it into another bean:

import org.springframework.stereotype.Service;

@Service
public class GreetingService {

    private final AppProperties appProperties;

    public GreetingService(AppProperties appProperties) {
        this.appProperties = appProperties;
    }

    public void printGreeting() {
        System.out.println("Welcome to " + appProperties.getName());
        System.out.println("Version: " + appProperties.getVersion());
        System.out.println(appProperties.getDescription());
    }
}

5. External Files Outside the JAR

You can override configuration from outside the application.

For Spring Boot:

java -jar myapp.jar --spring.config.location=file:/opt/myapp/application.properties

Or include an additional config file:

java -jar myapp.jar --spring.config.additional-location=file:/opt/myapp/

Example external file:

app.name=Production App
app.version=2.0.0
app.description=Running with production configuration

6. Environment Variables

Spring Boot can read environment variables automatically.

For example, this property:

app.name=My Spring App

Can be overridden with:

APP_NAME=Production App

Spring Boot maps environment variable names to property names using relaxed binding:

APP_NAME -> app.name
SERVER_PORT -> server.port
SPRING_DATASOURCE_URL -> spring.datasource.url

7. Command-Line Arguments

You can override properties when starting the application:

java -jar myapp.jar --app.name="Command Line App" --server.port=9090

Command-line arguments usually have high priority and override values from property files.


8. Profiles for Environment-Specific Config

Profiles let you separate configuration by environment.

Common files:

application.properties
application-dev.properties
application-test.properties
application-prod.properties

Example:

# application-prod.properties
app.name=Production App
server.port=8080

Run with a profile:

java -jar myapp.jar --spring.profiles.active=prod

Or set an environment variable:

SPRING_PROFILES_ACTIVE=prod

9. Default Values with @Value

You can provide fallback values:

@Value("${app.name:Default App}")
private String appName;

If app.name is missing, Spring uses "Default App".


10. Common Property Examples

server.port=8081

spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=myuser
spring.datasource.password=secret

logging.level.org.springframework=INFO

app.name=My Spring App
app.version=1.0.0

Summary

Use external configuration like this:

Use case Recommended approach
Simple single value @Value("${property.name}")
Group of related settings @ConfigurationProperties
Spring Boot default config application.properties or application.yml
Plain Spring Java config @PropertySource
Environment-specific config Spring profiles
Production overrides external file, environment variables, or command-line args

For most Spring Boot applications, the usual setup is:

src/main/resources/application.properties

plus a configuration class using:

@ConfigurationProperties(prefix = "app")

This keeps configuration clean, type-safe, and easy to override per environment.