Deadlock là tình huống trong lập trình đa luồng khi hai hoặc nhiều luồng (threads) bị mắc kẹt vì chờ đợi lẫn nhau để giải phóng các tài nguyên. Khi các luồng bị mắc kẹt sẽ dẫn đến tình trạng bế tắc, không thể tiếp tục thực thi các luồng và gây ảnh hưởng tới hiệu suất làm việc và tính ổn định của ứng dụng.

1. Deadlock trong Java là gì ?

Deadlock trong Java là một điều kiện khi hai hoặc nhiều luồng cố gắng truy cập vào cùng một tài nguyên tại cùng một khoảng thời gian. Sau đó, các luồng này không bao giờ có thể truy cập tài nguyên nữa mà bị chuyển sang trạng thái chờ mãi mãi. Vì vậy, deadlock phát sinh khi có nhiều hơn hai luồng hoặc hơn hai nguồn. Về cơ bản, deadlock xảy ra khi đa luồng yêu cầu cùng một nguồn nhưng chúng được nhận theo thứ tự khác nhau. Cuối cùng, chúng bị mắc kẹt trong một khoảng thời gian vô hạn và tạo ra deadlock.

Xem thêm bài viết về chủ đề ngôn ngữ lập trình Java:

- Tìm hiểu về Singleton pattern trong Java

- Tìm hiểu về design pattern factory trong java

- Xử lý timezone trong Java

2. Phát hiện Deadlock trong Java như thế nào ?

2.1. Ví dụ về Deadlock trong Java

public class DeadlockDemo {

  public static void main(String[] args) {

    final Object lock1 = new Object() ";
    final Object lock2 = new Object();

    Thread t1 = new Thread() {
      public void run() {
        synchronized(lock1) {
          System.out.println("Thread 1: Locked lock1");
          try {
            Thread.sleep(100);
          }
          catch(Exception e) {}
          synchronized(lock2) {
            System.out.println("Thread 1: Locked lock2");
          }
        }
      }
    };

    Thread t2 = new Thread() {
      public void run() {
        synchronized(lock1) {
          System.out.println("Thread 2: Locked lock1");
          try {
            Thread.sleep(100);
          } catch(Exception e) {}
          synchronized(lock2) {
            System.out.println("Thread 2: Locked lock2");
          }
        }
      }
    };
    t1.start();
    t2.start();
  }

}

Trong ví dụ trên, chúng ta có thể thấy thread t1 thực hiện lock lock1 và thực hiện sleep, sau đó lock lock2 để tiếp tục thực hiện. Trong khi đó thread t2 cũng thực hiện điều ngược lại là lock lock2 và sleep sau đó thực hiệc lock lock1. Tại đây, chúng ta có thể thấy 2 thread thực hiện lock tài nguyên và chờ nhau giải phóng, do đó tình trạng deadlock sẽ xảy ra. Các luồng sẽ bị tắc nghẽn và không thể tiếp tục thực hiện được công việc của nó nữa.

2.2. Cách phát hiện Deadlock trong Java

Chúng ta có thể phát hiện deadlock trong Java trong hệ thống bằng cách chạy chương trình trên và tìm kiếm trong ứng dụng Java Thread Dump. Chúng ta có thể chạy chương trình trong câu lệnh và thu thập kết xuất luồng và tùy thuộc vào Hệ điều hành, chúng ta có thể tạo kết xuất luồng bằng các lệnh. Chúng ta sẽ cần kiểm tra thread dump để xem các luồng có đang block nhau hay không. Để lấy được thông tin này chúng ta cần tìm được pid của process bằng cách dùng lệnh ps và thực hiện grep theo tên của process. Sau khi đã có được pid của tiến trình Java, chúng ta sẽ viết lệnh jcmd PID Thread .dump để tìm Deadlock.

3. Làm sao để phòng tránh Deadlock

Tránh khóa lồng nhau

Sử dụng khóa lồng nhau có thể là nguyên nhân chính dẫn đến deadlock trong Java. Chúng ta có thể tránh việc sử dụng khóa lồng nhau để ngăn deadlock trong Java. Khóa lồng nhau có nghĩa là chúng ta cung cấp truy cập từ các nguồn tới đa luồng. Nếu chúng ta đã chỉ định một khóa tới một luồng sau đó chúng ta tránh đứa nó tới một luồng khác.

Tránh các khóa không cần thiết

Chúng ta nên tránh đưa các khóa tới các thành viên hoặc luồng không cần nó. Chúng ta chỉ nên cung cấp khóa tới các luồng quan trọng và tránh sử dụng các khóa không cần thiết. Nếu chúng ta cung cấp một khóa không cần thiết tới một luồng không thực sự cần nó, thì nó có thể dẫn tới deadlock trong Java.

Sử dụng thread joins

Điều kiện dẫn đến deadlock thường xuyên xảy ra khi một luồng đang đợi một luồng khác để hoàn thành thực thi của nó và giữ lấy tài nguyên đó. Trong trường hợp này, chúng ta có thể sử dụng phương thức Thread.join() và cho nó một khoảng thời gian tối đa mà một luồng cần cần để hoàn thành việc thực thi. Điều này giúp chúng ta loại bỏ nguy cơ xảy ra deadlock trong Java.

4. Những điểm cần chú ý về Deadlock trong Java

  • Nếu hai hoặc ba luồng đang đợi cùng một nguồn hoặc khóa, sau đó nó sẽ là nền tảng dẫn đến deadlock trong Java.
  • Điều kiện dẫn đến deadlock xảy ra chỉ khi môi trường đa luồng nơi đa luồng được thực thi đồng thời.
  • Trong deadlock, các tiến trình không thể giải quyết tình huống bằng cách thay đổi trạng thái của mình. Chúng không thể giải phóng tài nguyên đang giữ mà cần thiết để phục vụ tiến trình khác.
  • Chúng ta hoàn toàn có thể can thiệp từ bên ngoài để giải quyết bài toán Deadlock bằng cách sử dụng các thuật toán khóa(synchronized, Lock) với thuật toán được thiết kế đúng từ ban đầu.

Kết luận:

Trong bài viết này, chúng ta đã tìm hiểu về deadlock trong Java - một trong những yếu tố quan trọng trong môi trường đa luồng. Hy vọng bạn có thể hiểu rõ hơn về deadlock để có thể tránh tình trạng này xảy ra trong dự án của mình.

Stringee hiện là đơn vị cung cấp bộ giải pháp giao tiếp Communication APIs giúp doanh nghiệp dễ dàng tích hợp các tính năng giao tiếp như gọi thoại/gọi video/chat vào ứng dụng hoặc website sẵn trong thời gian ngắn với chi phí tối ưu nhất. Giải pháp này cũng được hỗ trợ cho nền tảng React Native, chi tiết xin mời quý bạn đọc xem thêm tại đây.