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

