Spring Boot 3.4 Virtual Threads: Pertanyaan Wawancara dan Benchmark Performa

Kuasai Java 21 Virtual Threads dengan Spring Boot 3.4: 15 pertanyaan wawancara, benchmark performa, dan pola migrasi untuk lulus wawancara teknis.

Spring Boot 3.4 Virtual Threads: pertanyaan wawancara dan benchmark performa

Virtual Threads merupakan salah satu peningkatan paling penting di Java 21, dan Spring Boot 3.4 mengintegrasikannya secara native. Fitur Project Loom ini mengubah cara penanganan concurrency di aplikasi backend. Wawancara teknis kini menilai pemahaman atas mekanisme internal, kasus penggunaan yang sesuai, dan jebakan umum yang harus dihindari.

Tips Persiapan

Pewawancara membedakan kandidat yang memahami Virtual Threads dari mereka yang menggunakannya secara membabi buta. Mengetahui kapan TIDAK menggunakannya sama pentingnya dengan mengetahui manfaatnya.

Dasar-Dasar Virtual Threads

Pertanyaan 1: Apa itu Virtual Thread dan apa bedanya dengan Platform Thread?

Virtual Thread adalah thread ringan yang dikelola oleh JVM, bukan oleh sistem operasi. Berbeda dengan Platform Thread (thread tradisional), Virtual Thread tidak dipetakan langsung ke thread OS. JVM dapat menciptakan jutaan Virtual Thread dengan konsumsi memori yang minimal.

VirtualThreadDemo.javajava
// Comparing thread creation approaches
public class VirtualThreadDemo {

    public void demonstrateDifference() {
        // Platform Thread: ~1MB stack per thread
        // Practical limit: a few thousand on a standard JVM
        Thread platformThread = new Thread(() -> {
            performBlockingOperation();
        });

        // Virtual Thread: ~a few KB per thread
        // Can create millions without issues
        Thread virtualThread = Thread.ofVirtual().start(() -> {
            performBlockingOperation();
        });
    }

    // A Virtual Thread "mounts" onto a Platform Thread (carrier)
    // During I/O blocking, it releases the carrier for other Virtual Threads
    private void performBlockingOperation() {
        try {
            Thread.sleep(1000); // Virtual Thread detaches from carrier here
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

Perbedaan mendasarnya terletak pada perilaku saat blocking. Ketika Platform Thread mem-block (I/O, sleep), thread tersebut tetap terikat pada thread OS. Virtual Thread justru "unmount" dari carrier thread sehingga carrier bisa dipakai oleh Virtual Thread lain.

Pertanyaan 2: Bagaimana mengaktifkan Virtual Threads di Spring Boot 3.4?

Spring Boot 3.4 menyederhanakan aktivasi Virtual Threads menjadi satu properti konfigurasi. Seluruh framework menyesuaikan secara otomatis: Tomcat, REST controller, dan pemanggilan blocking langsung mendapatkan optimasi ini.

yaml
# application.yml
# Global Virtual Threads activation
spring:
  threads:
    virtual:
      enabled: true

# Optional Tomcat pool configuration
server:
  tomcat:
    threads:
      max: 200  # Less critical with Virtual Threads
      min-spare: 10
WebConfig.javajava
// Programmatic activation if needed
@Configuration
public class WebConfig {

    @Bean
    public TomcatProtocolHandlerCustomizer<?> virtualThreadCustomizer() {
        // Each HTTP request runs on a Virtual Thread
        return protocolHandler -> {
            protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
        };
    }
}

Aktivasi mengubah perilaku Tomcat: alih-alih thread pool tetap, setiap request mendapat Virtual Thread sendiri. Pendekatan ini menghilangkan bottleneck thread pool tradisional.

Pertanyaan 3: Jelaskan konsep "mounting" dan "unmounting"

Mounting menggambarkan proses penempelan Virtual Thread ke carrier thread (Platform Thread). Unmounting terjadi saat operasi blocking dan melepaskan carrier untuk Virtual Thread lain. Mekanisme ini memungkinkan penggunaan sumber daya CPU yang optimal.

MountingDemo.javajava
// Illustrating the mounting/unmounting cycle
public class MountingDemo {

    public void demonstrateMounting() {
        Thread.ofVirtual().start(() -> {
            // MOUNTED: Virtual Thread is using a carrier thread
            System.out.println("Carrier: " + getCurrentCarrier());

            // UNMOUNTING: releases carrier during blocking
            performDatabaseQuery(); // Blocking JDBC call

            // REMOUNTED: may be on a different carrier
            System.out.println("New carrier: " + getCurrentCarrier());
        });
    }

    // Blocking operations trigger unmounting automatically
    private void performDatabaseQuery() {
        // JDBC connection, file read, network call...
        // Virtual Thread detaches during I/O wait
    }

    private String getCurrentCarrier() {
        // Gets the current carrier thread name
        return Thread.currentThread().toString();
    }
}

Mekanisme ini bersifat transparan bagi developer. Kode tetap ditulis dengan gaya imperatif klasik, tetapi JVM mengoptimalkan penggunaan carrier secara otomatis. Pool berisi beberapa carrier sudah cukup untuk melayani jutaan Virtual Thread.

Catatan Teknis

Jumlah carrier thread secara default sama dengan jumlah core CPU. JVM mengatur pool ini secara dinamis melalui ForkJoinPool.

Kasus Penggunaan dan Anti-pattern

Pertanyaan 4: Kapan Virtual Threads memberikan peningkatan performa?

Virtual Threads bersinar untuk beban kerja I/O-bound: pemanggilan REST eksternal, query database, pembacaan file. Operasi-operasi tersebut menghabiskan sebagian besar waktu menunggu, dan saat itulah Virtual Thread melepaskan carrier-nya.

IOBoundService.javajava
// Ideal case for Virtual Threads
@Service
public class IOBoundService {

    private final RestClient restClient;
    private final UserRepository userRepository;

    // Each call involves network and database waiting
    // Virtual Threads shine here
    public UserProfile enrichUserProfile(Long userId) {
        // DB call - VT detaches during SQL query
        User user = userRepository.findById(userId).orElseThrow();

        // External REST call - VT detaches during HTTP wait
        ExternalData externalData = restClient
            .get()
            .uri("/api/external/{id}", userId)
            .retrieve()
            .body(ExternalData.class);

        // Data aggregation
        return new UserProfile(user, externalData);
    }
}

Keuntungannya berasal dari kemampuan menangani jauh lebih banyak request bersamaan. Dengan 200 Platform Thread dan request 100ms, throughput maksimum adalah 2.000 req/s. Dengan Virtual Threads, angka itu bisa mencapai lebih dari 50.000 req/s pada mesin yang sama.

Pertanyaan 5: Apa anti-pattern yang harus dihindari saat memakai Virtual Threads?

Virtual Threads tidak cocok untuk beban CPU-bound atau situasi yang menyebabkan "pinning". Pinning terjadi ketika Virtual Thread tetap menempel pada carrier-nya meskipun terjadi blocking, sehingga manfaat virtualisasi hilang.

AntiPatterns.javajava
// Examples of cases to avoid
@Service
public class AntiPatterns {

    // ANTI-PATTERN 1: CPU-intensive computation
    // Virtual Threads provide no benefit here
    public BigInteger computeFactorial(int n) {
        // 100% CPU, no I/O, no unmounting possible
        BigInteger result = BigInteger.ONE;
        for (int i = 2; i <= n; i++) {
            result = result.multiply(BigInteger.valueOf(i));
        }
        return result; // Carrier is monopolized throughout
    }

    // ANTI-PATTERN 2: Synchronized causes pinning
    private final Object lock = new Object();

    public void pinnedOperation() {
        synchronized (lock) { // PINNING: VT stays on carrier
            performDatabaseQuery(); // Unmounting doesn't happen!
        }
    }

    // SOLUTION: Use ReentrantLock
    private final ReentrantLock reentrantLock = new ReentrantLock();

    public void unpinnedOperation() {
        reentrantLock.lock();
        try {
            performDatabaseQuery(); // Unmounting possible
        } finally {
            reentrantLock.unlock();
        }
    }
}

Pinning membuat Virtual Thread setara dengan Platform Thread dari sudut pandang sumber daya. Penyebab utamanya adalah blok synchronized dan pemanggilan native JNI. Migrasi ke ReentrantLock mengatasi kasus pertama.

Pertanyaan 6: Bagaimana mendeteksi pinning dalam aplikasi?

JVM menyediakan opsi diagnostik untuk mengidentifikasi kasus pinning. Informasi ini sangat penting saat melakukan migrasi ke Virtual Threads, sebab pinning bisa memperburuk performa alih-alih meningkatkannya.

PinningDiagnostics.javajava
// Configuration and pinning detection
public class PinningDiagnostics {

    // JVM option to log pinning
    // -Djdk.tracePinnedThreads=full (detailed)
    // -Djdk.tracePinnedThreads=short (summary)

    // Example code causing pinning
    public void demonstratePinning() {
        Thread.ofVirtual().start(() -> {
            synchronized (this) {
                // This log will appear with tracePinnedThreads enabled
                try {
                    Thread.sleep(100); // Pinned during sleep!
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });
    }
}
bash
# Typical output with -Djdk.tracePinnedThreads=full
Thread[#23,VirtualThread[#1]/runnable@ForkJoinPool-1-worker-1,5,CarrierThreads]
    java.base/java.lang.VirtualThread$VThreadContinuation.onPinned(VirtualThread.java:183)
    java.base/java.lang.VirtualThread.parkOnCarrierThread(VirtualThread.java:661)
    com.example.PinningDiagnostics.demonstratePinning(PinningDiagnostics.java:15)

Analisis log pinning mengungkap titik panas yang harus diperbaiki. Aplikasi dengan pinning yang sering tidak memanfaatkan Virtual Threads sepenuhnya bahkan bisa lebih lambat dibandingkan Platform Threads.

Siap menguasai wawancara Spring Boot Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

Benchmark Performa

Pertanyaan 7: Berapa peningkatan performa yang bisa diharapkan dari Virtual Threads?

Benchmark menunjukkan peningkatan signifikan untuk aplikasi I/O-bound yang umum. Besarnya peningkatan tergantung pada rasio waktu I/O terhadap waktu CPU dan tingkat concurrency yang dibutuhkan.

BenchmarkController.javajava
// Endpoint for measuring performance
@RestController
@RequestMapping("/api/benchmark")
public class BenchmarkController {

    private final ExternalApiClient apiClient;

    // Simulation of a typical endpoint with external calls
    @GetMapping("/user/{id}")
    public ResponseEntity<UserData> getUser(@PathVariable Long id) {
        // Three sequential I/O calls
        UserInfo info = apiClient.fetchUserInfo(id);        // ~50ms
        List<Order> orders = apiClient.fetchOrders(id);     // ~80ms
        CreditScore score = apiClient.fetchCreditScore(id); // ~100ms

        return ResponseEntity.ok(new UserData(info, orders, score));
    }
}
text
# Benchmark results - 10,000 concurrent requests
# Configuration: 8 cores, 16GB RAM, simulated latency 230ms/request

Platform Threads (pool 200):
- Throughput: 850 req/s
- P99 latency: 1250ms
- Heap memory: 2.1GB

Virtual Threads:
- Throughput: 4200 req/s
- P99 latency: 280ms
- Heap memory: 850MB

Gain: 5x throughput, 4.5x P99 latency reduction

Virtual Threads juga menurunkan konsumsi memori karena setiap thread hanya menempati beberapa KB, bukan sekitar 1 MB seperti Platform Thread.

Pertanyaan 8: Bagaimana mengonfigurasi connection pool dengan Virtual Threads?

Connection pool (HikariCP, Lettuce) menjadi bottleneck baru saat memakai Virtual Threads. Pool 10 koneksi membatasi 10 query database secara bersamaan, bahkan dengan jutaan Virtual Thread sekalipun.

yaml
# application.yml
# HikariCP configuration optimized for Virtual Threads
spring:
  datasource:
    hikari:
      # More connections since Virtual Threads allow more concurrency
      maximum-pool-size: 50
      minimum-idle: 10
      # Shorter timeout due to more concurrent requests
      connection-timeout: 5000
      # Fast validation
      validation-timeout: 3000

  # Redis with Lettuce - already async-friendly
  data:
    redis:
      lettuce:
        pool:
          max-active: 50
          max-idle: 20
ConnectionPoolMonitor.javajava
// Pool monitoring to avoid contention
@Component
public class ConnectionPoolMonitor {

    private final HikariDataSource dataSource;

    @Scheduled(fixedRate = 10000)
    public void logPoolStats() {
        HikariPoolMXBean pool = dataSource.getHikariPoolMXBean();
        log.info("Pool stats - Active: {}, Idle: {}, Waiting: {}",
            pool.getActiveConnections(),
            pool.getIdleConnections(),
            pool.getThreadsAwaitingConnection());

        // Alert if too many threads waiting
        if (pool.getThreadsAwaitingConnection() > 100) {
            log.warn("Connection pool contention detected!");
        }
    }
}

Ukuran pool ditentukan oleh kapasitas database, bukan oleh jumlah Virtual Thread. PostgreSQL standar mendukung sekitar 100-200 koneksi aktif.

Pertanyaan 9: Bagaimana mengukur dampak Virtual Threads dengan Micrometer?

Micrometer dan Spring Boot Actuator menyediakan metrik penting untuk menilai efektivitas Virtual Threads. Metrik ini memvalidasi peningkatan dan menyingkap potensi masalah.

VirtualThreadMetrics.javajava
// Custom metrics for Virtual Threads
@Component
public class VirtualThreadMetrics {

    private final MeterRegistry registry;
    private final AtomicLong activeVirtualThreads = new AtomicLong(0);

    @PostConstruct
    public void registerMetrics() {
        // Active Virtual Threads counter
        Gauge.builder("virtual.threads.active", activeVirtualThreads, AtomicLong::get)
            .description("Number of active virtual threads")
            .register(registry);

        // JVM metrics for carriers
        Gauge.builder("virtual.threads.carriers", this::getCarrierCount)
            .description("Number of carrier threads")
            .register(registry);
    }

    private double getCarrierCount() {
        // Gets the ForkJoinPool carrier count
        return ForkJoinPool.commonPool().getPoolSize();
    }

    // Interceptor to trace requests
    public void trackVirtualThread(Runnable task) {
        activeVirtualThreads.incrementAndGet();
        try {
            task.run();
        } finally {
            activeVirtualThreads.decrementAndGet();
        }
    }
}
yaml
# application.yml
# Metrics exposure
management:
  endpoints:
    web:
      exposure:
        include: health,metrics,prometheus
  metrics:
    tags:
      application: ${spring.application.name}

Analisis metrik memperlihatkan rasio Virtual Threads / Carriers dan mendeteksi periode contention pada connection pool.

Migrasi dan Kompatibilitas

Pertanyaan 10: Library apa saja yang kompatibel dengan Virtual Threads?

Kompatibilitas tergantung pada penggunaan blok synchronized dan pemanggilan native. Ekosistem Spring sebagian besar sudah kompatibel, namun beberapa library memerlukan versi tertentu.

CompatibilityCheck.javajava
// Checking dependency compatibility
@Configuration
public class CompatibilityCheck {

    // Compatible libraries (recommended versions)
    // - Spring Boot 3.2+ (native support)
    // - HikariCP 5.1+ (ReentrantLock instead of synchronized)
    // - Lettuce 6.3+ (non-blocking I/O)
    // - Jackson 2.16+ (no synchronized)

    // Libraries requiring attention
    // - JDBC drivers: check version
    // - Some legacy HTTP clients

    @Bean
    public CommandLineRunner checkCompatibility() {
        return args -> {
            // Log versions for audit
            log.info("Java version: {}", System.getProperty("java.version"));
            log.info("Virtual threads available: {}",
                Thread.ofVirtual() != null);

            // Support verification
            if (Runtime.version().feature() < 21) {
                throw new IllegalStateException(
                    "Java 21+ required for Virtual Threads");
            }
        };
    }
}

Mayoritas framework modern sudah bermigrasi ke ReentrantLock. Untuk dependensi lawas, load test dengan -Djdk.tracePinnedThreads=short akan mengungkap masalah pinning.

Pertanyaan 11: Bagaimana melakukan migrasi bertahap ke Virtual Threads?

Migrasi bertahap memungkinkan validasi peningkatan sekaligus identifikasi masalah tanpa mengorbankan produksi. Strategi yang direkomendasikan adalah memisahkan endpoint Virtual Threads dari endpoint tradisional.

DualExecutorConfig.javajava
// Configuration for progressive migration
@Configuration
public class DualExecutorConfig {

    // Traditional executor for legacy endpoints
    @Bean("platformExecutor")
    public ExecutorService platformExecutor() {
        return Executors.newFixedThreadPool(200);
    }

    // Virtual Threads executor for new endpoints
    @Bean("virtualExecutor")
    public ExecutorService virtualExecutor() {
        return Executors.newVirtualThreadPerTaskExecutor();
    }
}

@RestController
@RequestMapping("/api/v2")
public class MigratedController {

    @Qualifier("virtualExecutor")
    private final ExecutorService executor;

    // Endpoint migrated to Virtual Threads
    @GetMapping("/users/{id}")
    public CompletableFuture<User> getUser(@PathVariable Long id) {
        return CompletableFuture.supplyAsync(() -> {
            // Business code unchanged
            return userService.findById(id);
        }, executor);
    }
}
yaml
# application.yml
# Feature flag for migration
features:
  virtual-threads:
    enabled: true
    endpoints:
      - /api/v2/**
      - /api/reports/**

# Disable for quick rollback
# features.virtual-threads.enabled: false
Perhatian

Jangan mengaktifkan Virtual Threads secara global sebelum semua dependensi diuji. Pinning dapat menurunkan performa secara signifikan.

Pertanyaan 12: Apa saja pengujian sebelum rilis ke produksi?

Validasi Virtual Threads memerlukan load test, pengujian pinning, dan pengujian kompatibilitas. Pengujian tersebut harus mencerminkan kondisi produksi yang realistis.

VirtualThreadLoadTest.javajava
// Load test with JUnit and Virtual Threads
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class VirtualThreadLoadTest {

    @LocalServerPort
    private int port;

    @Test
    void shouldHandleHighConcurrency() throws Exception {
        int concurrentRequests = 5000;
        CountDownLatch latch = new CountDownLatch(concurrentRequests);
        AtomicInteger successCount = new AtomicInteger(0);
        AtomicInteger errorCount = new AtomicInteger(0);

        HttpClient client = HttpClient.newHttpClient();

        // Launch 5000 simultaneous requests
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < concurrentRequests; i++) {
                executor.submit(() -> {
                    try {
                        HttpRequest request = HttpRequest.newBuilder()
                            .uri(URI.create("http://localhost:" + port + "/api/test"))
                            .build();

                        HttpResponse<String> response = client.send(
                            request, BodyHandlers.ofString());

                        if (response.statusCode() == 200) {
                            successCount.incrementAndGet();
                        } else {
                            errorCount.incrementAndGet();
                        }
                    } catch (Exception e) {
                        errorCount.incrementAndGet();
                    } finally {
                        latch.countDown();
                    }
                });
            }
        }

        latch.await(60, TimeUnit.SECONDS);

        // Performance assertions
        assertThat(successCount.get()).isGreaterThan(4900); // >98% success
        assertThat(errorCount.get()).isLessThan(100);
    }
}

Pengujian harus mencakup skenario contention (connection pool jenuh), timeout, dan beban yang berkelanjutan selama beberapa menit.

Pertanyaan Lanjutan

Pertanyaan 13: Bagaimana Virtual Threads berinteraksi dengan Structured Concurrency?

Structured Concurrency (JEP 453) melengkapi Virtual Threads dengan memastikan task konkuren memiliki siklus hidup yang sama. Pendekatan ini menyederhanakan penanganan error dan pembatalan.

StructuredConcurrencyExample.javajava
// Combining Virtual Threads + Structured Concurrency
public class StructuredConcurrencyExample {

    public UserDashboard fetchDashboard(Long userId) throws Exception {
        // StructuredTaskScope ensures all tasks complete together
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {

            // Three parallel tasks on Virtual Threads
            Subtask<UserProfile> profileTask = scope.fork(() ->
                userService.getProfile(userId));

            Subtask<List<Notification>> notifTask = scope.fork(() ->
                notificationService.getRecent(userId));

            Subtask<AccountBalance> balanceTask = scope.fork(() ->
                accountService.getBalance(userId));

            // Wait for all tasks or fail on first error
            scope.join();
            scope.throwIfFailed();

            // All tasks succeeded - safe aggregation
            return new UserDashboard(
                profileTask.get(),
                notifTask.get(),
                balanceTask.get()
            );
        }
        // If one task fails, others are automatically cancelled
    }
}

Structured Concurrency mencegah kebocoran thread dan memudahkan debugging karena call stack mencerminkan struktur logis kode.

Pertanyaan 14: Apa perbedaan antara Virtual Threads dan reactive programming?

Kedua pendekatan menyelesaikan masalah yang sama (efisiensi I/O), namun dengan model pemrograman yang berbeda. Virtual Threads memungkinkan kode imperatif klasik, sedangkan reactive programming mengharuskan penulisan ulang dalam bentuk stream.

ComparisonService.javajava
// Same logic: imperative vs reactive
@Service
public class ComparisonService {

    // VIRTUAL THREADS APPROACH: classic imperative code
    // Easy to read, debug, and maintain
    public UserData getUserDataImperative(Long id) {
        User user = userRepository.findById(id).orElseThrow();
        List<Order> orders = orderRepository.findByUserId(id);
        PaymentInfo payment = paymentService.getInfo(id);

        return new UserData(user, orders, payment);
    }

    // REACTIVE APPROACH: streams and operators
    // More complex but native backpressure
    public Mono<UserData> getUserDataReactive(Long id) {
        return userRepository.findById(id)
            .zipWith(orderRepository.findByUserId(id).collectList())
            .zipWith(paymentService.getInfo(id))
            .map(tuple -> new UserData(
                tuple.getT1().getT1(),
                tuple.getT1().getT2(),
                tuple.getT2()
            ));
    }
}

| Kriteria | Virtual Threads | Reactive | |----------|-----------------|----------| | Kurva belajar | Rendah | Tinggi | | Debugging | Stack trace klasik | Kompleks | | Backpressure | Manual | Native | | Ekosistem | Berkembang | Matang | | Migrasi legacy | Sederhana | Penulisan ulang |

Virtual Threads direkomendasikan untuk aplikasi baru maupun migrasi. Reactive tetap relevan ketika dibutuhkan backpressure yang kompleks.

Pertanyaan 15: Bagaimana menangani ThreadLocal dengan Virtual Threads?

ThreadLocal tetap dapat digunakan dengan Virtual Threads, namun menghabiskan memori untuk setiap instance. Scoped Values (JEP 446) menjadi alternatif yang lebih efisien untuk berbagi konteks.

ThreadLocalVsScopedValue.javajava
// Comparing context approaches
public class ThreadLocalVsScopedValue {

    // CLASSIC APPROACH: ThreadLocal
    // Works but expensive with millions of VT
    private static final ThreadLocal<RequestContext> requestContext =
        new ThreadLocal<>();

    public void processWithThreadLocal(Request request) {
        requestContext.set(new RequestContext(request.getTraceId()));
        try {
            // Context accessible everywhere in the thread
            processRequest();
        } finally {
            requestContext.remove(); // Important to prevent leaks
        }
    }

    // MODERN APPROACH: ScopedValue (Java 21+)
    // More efficient, immutable, explicit scope
    private static final ScopedValue<RequestContext> CONTEXT =
        ScopedValue.newInstance();

    public void processWithScopedValue(Request request) {
        ScopedValue.where(CONTEXT, new RequestContext(request.getTraceId()))
            .run(() -> {
                // Context accessible within this scope
                processRequest();
                // No cleanup needed - automatic at scope end
            });
    }

    private void processRequest() {
        // Context access
        String traceId = CONTEXT.isBound()
            ? CONTEXT.get().getTraceId()
            : "unknown";
        log.info("Processing with trace: {}", traceId);
    }
}

ScopedValues direkomendasikan untuk pengembangan baru. Untuk kode lawas yang masih memakai ThreadLocal, sebaiknya migrasi dilakukan secara bertahap.

Siap menguasai wawancara Spring Boot Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

Kesimpulan

Virtual Threads mengubah pengembangan backend Java dengan menghadirkan kode yang sederhana dan berkinerja tinggi. Poin penting:

Dasar:

  • Aktivasi via spring.threads.virtual.enabled=true
  • Ideal untuk beban I/O-bound (REST, DB, file)
  • Hindari untuk komputasi intensif CPU

Performa:

  • Atur ukuran connection pool secara tepat (bottleneck baru)
  • Pantau pinning dengan -Djdk.tracePinnedThreads
  • Migrasikan synchronized ke ReentrantLock

Migrasi:

  • Lakukan pengujian bertahap per kelompok endpoint
  • Validasi kompatibilitas dependensi
  • Manfaatkan Structured Concurrency untuk operasi paralel

Menguasai Virtual Threads membedakan kandidat yang memahami tantangan performa aplikasi modern. Konsep ini kini wajib dikuasai dalam wawancara teknis Spring Boot.

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Tag

#spring boot
#virtual threads
#java 21
#project loom
#performance

Bagikan

Artikel terkait