Dự án OpenJDK Loom

trieu.dev.da

Nguyễn Thanh Triều
1. Project Loom

Project Loom là một nỗ lực của cộng đồng OpenJDK nhằm giới thiệu một cấu trúc đồng thời nhẹ cho Java. Các nguyên mẫu cho Loom cho đến nay đã đưa ra một sự thay đổi trong JVM cũng như thư viện Java.


Mặc dù chưa có lịch phát hành cho Loom, nhưng chúng tôi có thể truy cập các nguyên mẫu gần đây trên wiki của Project Loom .


Trước khi thảo luận về các khái niệm khác nhau của Loom, hãy thảo luận về mô hình tương tranh hiện tại trong Java.

2. Java's Concurrency Model

Hiện tại, Thread đại diện cho sự trừu tượng hóa cốt lõi của sự tương tranh trong Java. Sự trừu tượng hóa này, cùng với các API đồng thời khác giúp dễ dàng viết các ứng dụng đồng thời.


Tuy nhiên, vì Java sử dụng các luồng nhân hệ điều hành để triển khai, nên nó không đáp ứng được yêu cầu đồng thời ngày nay. Đặc biệt có hai vấn đề lớn:


Các luồng không thể khớp với quy mô của đơn vị đồng thời của miền. Ví dụ: các ứng dụng thường cho phép tối đa hàng triệu giao dịch, người dùng hoặc phiên. Tuy nhiên, số lượng luồng được kernel hỗ trợ ít hơn nhiều. Do đó, một luồng T cho mọi người dùng, giao dịch hoặc phiên thường không khả thi. Hầu hết các ứng dụng đồng thời cần một số đồng bộ hóa giữa các luồng cho mọi yêu cầu. Do đó, một chuyển đổi bối cảnh đắt tiền xảy ra giữa các luồng hệ điều hành. Một giải pháp khả thi cho những vấn đề như vậy là sử dụng các API đồng thời không đồng bộ . Các ví dụ phổ biến là CompleteableFuture và RxJava . Với điều kiện là các API như vậy không chặn luồng nhân, nó cung cấp cho ứng dụng một cấu trúc đồng thời chi tiết hơn trên các luồng Java .


Mặt khác, các API như vậy khó gỡ lỗi và tích hợp hơn với các API cũ . Và do đó, cần có một cấu trúc đồng thời nhẹ, độc lập với các luồng nhân.

3. Tasks and Schedulers

Mọi triển khai của một luồng, nhẹ hoặc nặng, đều phụ thuộc vào hai cấu trúc:


  1. Nhiệm vụ (còn được gọi là phần tiếp theo) – Một chuỗi các hướng dẫn có thể tự tạm dừng đối với một số thao tác chặn
  2. Bộ lập lịch – Để chỉ định phần tiếp theo cho CPU và chỉ định lại CPU từ phần tiếp theo bị tạm dừng

Hiện tại, Java dựa vào việc triển khai hệ điều hành cho cả phần tiếp theo và bộ lập lịch biểu .


Bây giờ, để tạm dừng việc tiếp tục, cần phải lưu trữ toàn bộ ngăn xếp cuộc gọi. Và tương tự, truy xuất ngăn xếp cuộc gọi khi tiếp tục. Vì việc triển khai các phần tiếp theo của hệ điều hành bao gồm ngăn xếp cuộc gọi riêng cùng với ngăn xếp cuộc gọi của Java, nên nó dẫn đến một dấu chân nặng nề .


Tuy nhiên, một vấn đề lớn hơn là việc sử dụng bộ lập lịch hệ điều hành. Vì bộ lập lịch chạy ở chế độ nhân nên không có sự khác biệt giữa các luồng. Và nó xử lý mọi yêu cầu CPU theo cùng một cách.


Kiểu lập lịch trình này không phải là tối ưu cho các ứng dụng Java nói riêng .


Ví dụ: hãy xem xét một luồng ứng dụng thực hiện một số hành động đối với các yêu cầu và sau đó chuyển dữ liệu sang một luồng khác để xử lý thêm. Ở đây, sẽ tốt hơn nếu lên lịch cho cả hai luồng này trên cùng một CPU . Nhưng vì bộ lập lịch biểu không liên quan đến luồng yêu cầu CPU nên điều này là không thể đảm bảo.


Project Loom đề xuất giải quyết vấn đề này thông qua các luồng ở chế độ người dùng dựa trên việc triển khai các phần tiếp theo và bộ lập lịch trong thời gian chạy Java thay vì triển khai hệ điều hành .

4. Fibers

Trong các nguyên mẫu gần đây trong OpenJDK, một lớp mới có tên Fiber được đưa vào thư viện cùng với lớp Thread .


Do thư viện được lên kế hoạch cho Fibers tương tự như Thread , nên việc triển khai của người dùng cũng phải tương tự. Tuy nhiên, có hai điểm khác biệt chính:


Fiber sẽ bao bọc bất kỳ tác vụ nào trong quá trình tiếp tục ở chế độ người dùng nội bộ. Điều này sẽ cho phép tác vụ tạm dừng và tiếp tục trong thời gian chạy Java thay vì kernel Một bộ lập lịch chế độ người dùng có thể cắm ( ví dụ: ForkJoinPool ) sẽ được sử dụng Chúng ta hãy đi qua hai mục này một cách chi tiết.

5. Continuations

Sự tiếp tục (hoặc đồng quy trình) là một chuỗi các hướng dẫn có thể mang lại và được tiếp tục bởi người gọi ở giai đoạn sau.


Mỗi sự tiếp tục có một điểm vào và một điểm năng suất. Điểm năng suất là nơi nó bị đình chỉ. Bất cứ khi nào người gọi tiếp tục tiếp tục, điều khiển sẽ quay trở lại điểm năng suất cuối cùng.


Điều quan trọng là phải nhận ra rằng việc tạm dừng/tiếp tục này hiện xảy ra trong thời gian chạy ngôn ngữ thay vì OS . Do đó, nó ngăn cản việc chuyển ngữ cảnh tốn kém giữa các luồng nhân.


Tương tự như Thread, Project Loom nhằm mục đích hỗ trợ các fibers lồng nhau. Vì các fibers dựa vào các phần tiếp theo bên trong, nên nó cũng phải hỗ trợ các phần tiếp theo lồng nhau. Để hiểu điều này tốt hơn, hãy xem xét một lớp Tiếp tục cho phép lồng nhau:


Continuation cont1 = new Continuation(() -> {
Continuation cont2 = new Continuation(() -> {
//do something
suspend(SCOPE_CONT_2);
suspend(SCOPE_CONT_1);
});
});

Như được hiển thị ở trên, phần tiếp theo lồng nhau có thể tạm dừng chính nó hoặc bất kỳ phần tiếp theo kèm theo nào bằng cách chuyển một biến phạm vi . Vì lý do này, chúng được gọi là scoped continuations .


Vì việc tạm dừng một phần tiếp theo cũng sẽ yêu cầu nó lưu trữ ngăn xếp cuộc gọi, nên mục tiêu của dự án Loom là bổ sung tính năng truy xuất ngăn xếp nhẹ trong khi tiếp tục phần tiếp theo.

6. Scheduler

Trước đó, chúng ta đã thảo luận về những thiếu sót của bộ lập lịch hệ điều hành trong việc lập lịch các luồng có thể liên quan trên cùng một CPU.


Mặc dù mục tiêu của Project Loom là cho phép các bộ lập lịch có thể cắm được bằng cáp quang, nhưng ForkJoinPool ở chế độ không đồng bộ sẽ được sử dụng làm bộ lập lịch mặc định.


ForkJoinPool hoạt động trên thuật toán đánh cắp công việc . Do đó, mỗi luồng duy trì một nhiệm vụ deque và thực thi nhiệm vụ từ đầu của nó. Hơn nữa, bất kỳ luồng nhàn rỗi nào cũng không chặn, chờ tác vụ và thay vào đó kéo nó từ đuôi deque của luồng khác.


Sự khác biệt duy nhất trong chế độ không đồng bộ là các worker thread đánh cắp nhiệm vụ từ đầu của deque khác .


ForkJoinPool thêm một tác vụ được lên lịch bởi một tác vụ đang chạy khác vào hàng đợi cục bộ. Do đó, thực thi nó trên cùng một CPU.

7. Conclusion

Trong bài viết này, chúng ta đã thảo luận về các vấn đề trong Java's current concurrency model và những thay đổi do Project Loom đề xuất .


Khi làm như vậy, chúng tôi cũng đã xác định các tác vụ và bộ lập lịch biểu, đồng thời xem xét cách Fibers và ForkJoinPool có thể cung cấp giải pháp thay thế cho Java bằng cách sử dụng các luồng nhân.
 
Bên trên