0

[Java Backend Zero to Hello] BÀI 2.6: ĐA LUỒNG (MULTITHREADING)

Java Backend Zero to Hello

📚 Bài viết thuộc series Java Backend Zero to Hello 📌 Phần: Phase 2: Java Core Nâng Cao | Bài 21/86


BÀI 2.6: ĐA LUỒNG (MULTITHREADING)

Mục tiêu

  • Hiểu về Thread và đa luồng
  • Tạo Thread bằng nhiều cách
  • Đồng bộ hóa (synchronized)
  • Sử dụng volatile, wait/notify

1. PROCESS vs THREAD

Process Thread
Chương trình đang chạy Đơn vị nhỏ hơn trong process
Có bộ nhớ riêng Chia sẻ bộ nhớ với thread khác
Tốn tài nguyên Nhẹ hơn
Giao tiếp qua IPC Giao tiếp trực tiếp

2. TẠO THREAD

2.1 Kế thừa Thread

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread đang chạy: " + getName());
    }
}

MyThread t = new MyThread();
t.start();  // Bắt đầu thread mới

2.2 Implement Runnable

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable đang chạy");
    }
}

Thread t = new Thread(new MyRunnable());
t.start();

2.3 Lambda (Java 8+)

Thread t = new Thread(() -> {
    System.out.println("Lambda thread");
});
t.start();

2.4 Với Callable (trả về giá trị)

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

Callable<Integer> task = () -> {
    Thread.sleep(1000);
    return 42;
};

FutureTask<Integer> futureTask = new FutureTask<>(task);
Thread t = new Thread(futureTask);
t.start();

Integer result = futureTask.get();  // Blocking - chờ kết quả

3. CÁC METHOD CỦA THREAD

Thread t = new Thread(() -> {
    // ...
});

t.start();              // Bắt đầu thread
t.join();               // Chờ thread kết thúc
t.join(1000);           // Chờ tối đa 1000ms
t.setName("MyThread");  // Đặt tên
t.setPriority(Thread.MAX_PRIORITY);  // Ưu tiên 1-10
t.isAlive();            // Còn sống?
t.isDaemon();           // Là daemon?
t.interrupt();          // Ngắt thread

Daemon Thread

Thread chạy nền, JVM không chờ khi kết thúc.

Thread daemon = new Thread(() -> {
    while (true) {
        System.out.println("Daemon running");
        try { Thread.sleep(1000); } catch (InterruptedException e) {}
    }
});
daemon.setDaemon(true);
daemon.start();

4. VÒNG ĐỜI CỦA THREAD

NEW → RUNNABLE → BLOCKED/WAITING/TIMED_WAITING → TERMINATED
State Mô tả
NEW Mới tạo, chưa start
RUNNABLE Đang chạy hoặc sẵn sàng chạy
BLOCKED Chờ monitor lock
WAITING Chờ vô thời hạn (wait, join)
TIMED_WAITING Chờ có thời hạn (sleep, wait(timeout))
TERMINATED Đã kết thúc

5. ĐỒNG BỘ HÓA (SYNCHRONIZATION)

5.1 Vấn đề Race Condition

class Counter {
    private int count = 0;

    public void increment() {
        count++;  // Không atomic - có thể bị lỗi
    }
}

5.2 synchronized method

class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

5.3 synchronized block

class Counter {
    private int count = 0;
    private final Object lock = new Object();

    public void increment() {
        synchronized (lock) {
            count++;
        }
    }
}

5.4 static synchronized

class Counter {
    private static int count = 0;

    public static synchronized void increment() {
        count++;
    }
}

6. VOLATILE

Đảm bảo biến được đọc/ghi từ main memory (không cache).

class SharedData {
    private volatile boolean running = true;

    public void stop() {
        running = false;
    }

    public void run() {
        while (running) {
            // Làm gì đó
        }
    }
}

⚠️ volatile không thay thế synchronized - chỉ đảm bảo visibility, không đảm bảo atomicity.


7. WAIT, NOTIFY, NOTIFYALL

Giao tiếp giữa các thread.

class MessageQueue {
    private String message;
    private boolean hasMessage = false;

    public synchronized void put(String message) throws InterruptedException {
        while (hasMessage) {
            wait();  // Chờ consumer lấy
        }
        this.message = message;
        hasMessage = true;
        notifyAll();  // Đánh thức consumer
    }

    public synchronized String take() throws InterruptedException {
        while (!hasMessage) {
            wait();  // Chờ producer put
        }
        String msg = message;
        hasMessage = false;
        notifyAll();  // Đánh thức producer
        return msg;
    }
}

8. PRODUCER-CONSUMER VỚI BLOCKINGQUEUE

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;

BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);

// Producer
Thread producer = new Thread(() -> {
    try {
        for (int i = 0; i < 100; i++) {
            queue.put(i);
            System.out.println("Produced: " + i);
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

// Consumer
Thread consumer = new Thread(() -> {
    try {
        while (true) {
            Integer item = queue.take();
            System.out.println("Consumed: " + item);
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

producer.start();
consumer.start();

9. BÀI TẬP THỰC HÀNH

Bài 1: In số song song

// 2 thread in số chẵn/lẻ từ 1-100
Thread oddThread = new Thread(() -> {
    for (int i = 1; i <= 100; i += 2) {
        System.out.println("Odd: " + i);
    }
});

Thread evenThread = new Thread(() -> {
    for (int i = 2; i <= 100; i += 2) {
        System.out.println("Even: " + i);
    }
});

oddThread.start();
evenThread.start();

Bài 2: Bank Account an toàn

class BankAccount {
    private double balance;
    private final Object lock = new Object();

    public void deposit(double amount) {
        synchronized (lock) {
            balance += amount;
        }
    }

    public boolean withdraw(double amount) {
        synchronized (lock) {
            if (balance < amount) return false;
            balance -= amount;
            return true;
        }
    }

    public double getBalance() {
        synchronized (lock) {
            return balance;
        }
    }
}

Bài 3: Download song song

List<String> urls = Arrays.asList("url1", "url2", "url3");
List<String> results = new ArrayList<>();

ExecutorService executor = Executors.newFixedThreadPool(3);
List<Future<String>> futures = urls.stream()
    .map(url -> executor.submit(() -> download(url)))
    .collect(Collectors.toList());

for (Future<String> future : futures) {
    results.add(future.get());
}
executor.shutdown();

10. TÓM TẮT

Khái niệm Mô tả
Thread Đơn vị thực thi trong process
Runnable Task cho thread
synchronized Đồng bộ hóa
volatile Đảm bảo visibility
wait/notify Giao tiếp giữa thread
Daemon Thread nền
Race condition Lỗi khi nhiều thread truy cập

Bài tiếp theo: 2.7 Executor Framework


🧭 Điều Hướng Series

⬅️ Bài trước: BÀI 2.5: DATE/TIME API (Java 8+)

📋 Lộ trình tổng quan: Xem Toàn Bộ Series

➡️ Bài tiếp theo: BÀI 2.7: EXECUTOR FRAMEWORK


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí