Phát triển với Java thời gian thực - Phần 1: Khai thác các đặc tính độc nhất
Tóm tắt: Bộ Java thời gian thực (Real-time Java™) kết hợp dễ dàng việc lập
trình bằng ngôn ngữ Java theo hiệu năng do ứng dụng yêu cầu mà phải phù hợp
với các ràng buộc thời gian thực. Các phần mở rộng của ngôn ngữ Java đưa ra các
đặc tính về môi trường thời gian thực mà đang thiếu trong môi trường thời gian
chạy Java truyền thống. Bài này, bài đầu tiên trong loạt bài ba phần, mô tả một số
đặc tính này và giải thích cách bạn có thể áp dụng chúng để đạt được hiệu năng
thời gian thực trong các ứng dụng của chính mình.
Java thời gian thực là một bộ các tăng cường cho ngôn ngữ Java, cung cấp cho các
ứng dụng một mức hiệu năng thời gian thực, vượt trội hiệu năng của công nghệ
Java chuẩn. Hiệu năng thời gian thực khác với hiệu năng thông lượng truyền
thống, là một thước đo điển hình của tổng số các chỉ thị, tác vụ, hoặc công việc có
thể được thực hiện trong khoảng thời gian ấn định. Hiệu năng thời gian thực tập
trung vào thời gian mà một ứng dụng yêu cầu để đáp ứng các kích thích bên ngoài
mà không vượt quá các ràng buộc thời gian cho trước. Trong trường hợp của các
hệ thống thời gian thực cứng(hard real-time), các ràng buộc như vậy không bao
giờ được vượt quá; các hệ thống thời gian thực mềm(soft real-time) có một dung
saicao hơn đối với các vi phạm. Hiệu năng thời gian thực đòi hỏi chính ứng dụng
phải giành được quyền điều khiển của bộ xử lý sao cho nó có thể trả lời các kích
thích, và trong khi trả lời các tác nhân kích thích đó thì bộ mã của ứng dụng không
bị khóa do thực hiện các quy trình tương tranh trong máy ảo đó. Java thời gian
thực đưa ra độ đáp ứng mà trước đây chưa được thoả mãn trong các ứng dụng
Java.
ption e) { } finally { isConsuming = false; } } } Trong Liệt kê 5, các đối tượng tác nhân tạo ra và tác nhân tiêu thụ truy cập đến một hàng đợi của các sự kiện mà được mã hóa như một chuỗi các đối tượng java.lang.Integer. Bộ mã này chờ đợi ngữ cảnh cấp phát hiện tại trở thành một vùng nhớ được khoanh vùng và chờ các hàng đợi của các sự kiện để được lưu giữ như là đối tượng cổng Web (portal object) của vùng đó. (Cổng Web là một đối tượng được phân bổ từ vùng mà có thể được lưu giữ trong chính đối tượng vùng nhớ được khoanh vùng, một sự tiện lợi hữu ích vì các đối tượng được khoanh vùng không thể được lưu trong các trường tĩnh hoặc trong các đối tượng được phân bổ từ một vùng cha.) Nếu không tìm thấy hàng đợi, nó sẽ được tạo ra. Một cặp trường cấp phát nhanh được dùng để thông báo cho các xử lí quan tâm về tiến độ sản xuất và tiêu thụ các sự kiện. Hai lớp trong Liệt kê 6 trình bày cách mã trong Liệt kê 5 có thể được thực thi: Liệt kê 6. Các lớp có thể lập lịch class NoHeapHandler extends AsyncEventHandler { final MemoryArea sharedArea; final Producer producer; NoHeapHandler( PriorityScheduler scheduler, ScopedMemory sharedArea, Producer producer) { super(new PriorityParameters(scheduler.getMaxPriority()), null, null, null, null, true); this.sharedArea = sharedArea; this.producer = producer; } public void handleAsyncEvent() { sharedArea.enter(producer); } } class NoHeapThread extends NoHeapRealtimeThread { boolean terminate; final MemoryArea sharedArea; final Consumer consumer; NoHeapThread( PriorityScheduler scheduler, ScopedMemory sharedArea, Consumer consumer) { super(new PriorityParameters(scheduler.getNormPriority()), RealtimeThread.getCurrentMemoryArea()); this.sharedArea = sharedArea; this.consumer = consumer; } public synchronized void run() { try { while(true) { if(consumer.setConsuming) { sharedArea.enter(consumer); } else { synchronized(this) { if(!terminate) { if(!consumer.setConsuming) { wait(); } } else { break; } } } } } catch(InterruptedException e) {} } } Trong Liệt kê 6, bộ mã tác nhân tạo dữ liệu (data-producer) được chỉ định làm một bộ xử lý sự kiện không đồng bộ, sẽ được chạy ở quyền ưu tiên cao nhất đang có. Bộ xử lý này chỉ cần nhập vào một vùng nhớ được khoanh vùng để chạy bộ mã tác nhân tạo ra. Cũng vùng nhớ được khoanh vùng như vậy là một tham số cho một lớp NHRT mà hoạt động như tác nhân tiêu thụ của dữ liệu. Lớp xử lí cũng như vậy, cho phép truy cập đồng bộ đến các trường terminate và setConsuming cho hành vi ra lệnh. Khi xử lí tác nhân tiêu thụ đang tiêu thụ các sự kiện, nó nhập vào vùng nhớ được chia sẻ để thực thi mã tác nhân tiêu thụ, chạy với quyền ưu tiên thấp hơn tác nhân tạo ra. (Hành vi tiêu thụ trong thí dụ này là không quan trọng, chỉ đơn giản là in bộ định danh sự kiện ra bàn điều khiển giao diện.) Liệt kê 7 trình bày việc bộ mã khởi tạo hệ thống và thể hiện hành vi hệ thống: Liệt kê 7. Hành vi hệ thống public class EventSystem implements Runnable { public static void main(String args[]) throws InterruptedException { RealtimeThread systemThread = new RealtimeThread( null, null, null, new VTMemory(20000L), null, null) { public void run() { VTMemory systemArea = new VTMemory(20000L, new EventSystem()); systemArea.enter(); } }; systemThread.start(); } public void run() { try { PriorityScheduler scheduler = (PriorityScheduler) Scheduler.getDefaultScheduler(); VTMemory scopedArea = new VTMemory(20000L); Consumer consumer = new Consumer(); NoHeapThread thread = new NoHeapThread(scheduler, scopedArea, consumer); Producer producer = new Producer(thread); NoHeapHandler handler = new NoHeapHandler(scheduler, scopedArea, producer); AsyncEvent event = new AsyncEvent(); event.addHandler(handler); int handlerPriority = ((PriorityParameters) handler.getSchedulingParameters()).getPriority(); RealtimeThread.currentRealtimeThread().setPriority(handlerPriority - 1); thread.start(); waitForConsumer(consumer); //fire several events while there is a consumer event.fire(); event.fire(); event.fire(); waitForEvent(producer, 3); setConsuming(thread, false); //fire a couple of events while there is no consumer event.fire(); event.fire(); waitForEvent(producer, 5); setConsuming(thread, true); waitForConsumer(consumer); //fire another event while there is a consumer event.fire(); waitForEvent(producer, 6); synchronized(thread) { thread.terminate = true; setConsuming(thread, false); } } catch(InterruptedException e) {} } private void setConsuming(NoHeapThread thread, boolean enabled) { synchronized(thread) { thread.consumer.setConsuming = enabled; thread.notify(); } } private void waitForEvent(Producer producer, int eventNumber) throws InterruptedException { while(producer.eventIdentifier < eventNumber) { Thread.sleep(100); } } private void waitForConsumer(Consumer consumer) throws InterruptedException { while(!consumer.isConsuming) { Thread.sleep(100); } } } Trong Liệt kê 7, một cặp vùng được sử dụng làm cơ sở cho ngăn xếp vùng đối với xử lí và bộ xử lý không-đống, một yêu cầu vì các lớp Schedulable này không thể truy cập bất kỳ đối tượng nào mà được phân bổ đống (heap-allocated). Một đối tượng sự kiện không đồng bộ đại diện cho sự kiện, với bộ xử lý kèm theo để được gửi đi khi sự kiện được thải bỏ. Khi hệ thống được khởi tạo, mã này khởi động xử lí tác nhân tiêu thụ và thải bỏ sự kiện vài lần, chạy với quyền ưu tiên chỉ thấp hơn quyền ưu tiên của bộ xử lý sự kiện. Bộ mã cũng tắt và bật xử lí tác nhân tiêu thụ trong khi bổ sung các sự kiện bị thải bỏ. Liệt kê 8 hiển thị đầu ra khi EventSystem (Hệ thống Sự kiện) chạy trong một JVM thời gian thực: Liệt kê 8. Xuất qua bàn điều khiển 1 2 3 6 Một khía cạnh thú vị của thí dụ này là lý do các sự kiện 4 và 5 không được báo cáo. Mỗi khi xử lí nghe (listening thread) báo cáo về các sự kiện trong hàng đợi, nó khởi động từ phía trước hàng đợi và đi đến phần cuối, đề nghị rằng tất cả 6 sự kiện sẽ được báo cáo ít nhất là một lần. Tuy nhiên, thiết kế này đảm bảo rằng bộ nhớ được sử dụng để lưu các sự kiện được tự động loại bỏ khi không có xử lí nào tiêu thụ chúng. Khi một xử lí tác nhân tiêu thụ ngừng việc đọc từ hàng đợi, nó thoát ra vùng nhớ được khoanh vùng, vào lúc mà không có đối tượng Schedulable nào sử dụng vùng này làm ngữ cảnh cấp phát. Sự vắng mặt của các đối tượng Schedulable bằng cách sử dụng vùng này có nghĩa là vùng được khoanh vùng không còn đối tượng nào và được cài đặt lại. Việc này gồm cả đối tượng cổng Web, cho nên hàng đợi và tất cả các sự kiện trong nó được loại bỏ khi xử lí ngừng việc nghe. Mỗi khi một sự kiện tiếp sau bị thải bỏ, hàng đợi sẽ được tạo lại và được đưa dữ liệu vào lại, nhưng không có xử lí nghe, bộ nhớ được loại bỏ ngay lập tức sau đó. Việc quản lý bộ nhớ là tự động và chạy không bị bộ gom rác can thiệp, nếu bộ gom này là đang hoạt động (vì cả bộ xử lý và xử lí đều là không-đống.) Các sự kiện được lưu lại làm một hàng đợi của các đối tượng trong bộ nhớ, tiếp tục phát triển nếu một xử lí nghe sẵn có để tiêu thụ chúng. Nếu không sẵn có, hàng đợi và các sự kiện liên quan tự động được loại bỏ. Một kịch bản sử dụng tổng quát Với việc lập lịch và khung làm việc quản lý bộ nhớ, bạn có thể thiết kế ra một ứng dụng với các mức ưu tiên khác nhau cho các xử lí để thực hiện một cách tối ưu trong một máy ảo thời gian thực (và có đầy đủ khả năng trong các máy ảo khác). Ứng dụng có thể bao gồm các xử lí quản lí-sự kiện của mức ưu tiên cao, thu thập dữ liệu từ các đầu vào bên ngoài và lưu lại dữ liệu để xử lý. Do tính nhất thời và không đồng bộ của chúng, các xử lí quản lí-sự kiện này có lẽ thích hợp đối với việc quản lý bộ nhớ xen kẽ, và chúng có lẽ phụ thuộc nặng nhất vào các ràng buộc thời gian thực. Ở mức ưu tiên trung bình hẳn đang xử lý các xử lí mà tiêu thụ dữ liệu và tạo ra các tính toán, hoặc phân phối dữ liệu. Các xử lí mức trung bình có thể đòi hỏi áp dụng bộ xử lý trung tâm được phân bổ đầy đủ để quản lý các tải làm việc của chúng. Ở các mức ưu tiên thấp nhất, có thể có các xử lí duy trì và ghi nhật ký. Việc sử dụng một máy ảo thời gian thực để quản lý lập lịch và sử dụng bộ nhớ của các tác vụ khác nhau này trong ứng dụng có thể cho phép nó chạy hiệu quả nhất. Dự định của RTSJ là cho phép các nhà phát triển viết ra các ứng dụng chạy trong các ràng buộc thời gian thực được yêu cầu. Chỉ cần sử dụng bộ lập lịch và các xử lí thời gian thực là có thể đủ thực hiện được mục tiêu đó. Nếu không, sự phát triển hiện đại hơn có thể là cần thiết để tận dụng một hoặc nhiều đặc tính hiện đại hơn do máy ảo thực hiện. Kết luận Phần 1 Bài viết này đã phác thảo ra một số mẹo để bạn bắt đầu hoà nhập các phần tử của Java thời gian thực vào ứng dụng Java của bạn. Nó bao hàm một số đặc điểm lập lịch và quản lý bộ nhớ mà bạn hẳn muốn sử dụng để thực hiện hiệu năng thời gian thực. Đây là một điểm xuất phát để bạn tận dụng các lợi ích truyền thống của ngôn ngữ Java, chẳng hạn như sự tương tác và an toàn, và kết hợp chúng với các đặc tính mới cho phép bạn thoả mãn các ràng buộc thời gian thực mà ứng dụng của bạn đòi hỏi. Trong phần tiếp theo ở loạt bài này, bạn sẽ tìm hiểu các kỹ thuật để chuyển một ứng dụng hiện hành sang Java thời gian thực. Bài cuối sẽ xây dựng trên hai phần đầu và dẫn bạn qua việc thiết kế, xác thực, và gỡ lỗi một hệ thống thời gian thực mà hợp nhất với Java thời gian thực. Mục lục Các quy trình con phải bị ràng buộc Các xử lí thời gian thực Các vùng nhớ tách biệt Các tuỳ chọn để lập lịch mã nhạy thời gian Một thí dụ tổng hợp Một kịch bản sử dụng tổng quát Kết luận Phần 1
File đính kèm:
- Phát triển với Java thời gian thực, Phần 1 Khai thác các đặc tính độc nhất .pdf