Tải bản đầy đủ (.docx) (42 trang)

Báo cáo đồ án cơ sở ngành mạng

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.31 MB, 42 trang )

TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI
KHOA CÔNG NGHỆ THÔNG TIN
---    ---

BÁO CÁO ĐỒ ÁN
CƠ SỞ NGÀNH MẠNG
ĐỀ TÀI 1:

BÀI TOÁN NĂM TRIẾT GIA ĂN TỐI
ĐỀ TÀI 2:

TÌM HIỂU GIAO THỨC SMTP VÀ POP3
XÂY DỰNG MAIL CLIENT
Sinh viên thực hiện: Nguyễn Khánh
Lớp SH: 12T1
MSSV: 102120249
GVHD: ThS. Trần Hồ Minh

Đà Nẵng, tháng 5 năm 2016
Đánh giá của hội đồng duyệt đồ án:
.......................................................................................................................................................................................................


MỤC LỤC

LỜI MỞ ĐẦU
Máy tính điện tử ra đời là một cuộc cách mạng lớn của nhân loại, từ đó
giúp con người giảm bớt cơng sức trong việc tính tốn, nhằm nâng cao năng
suất và hiệu quả lao động. Thế nhưng sẽ chỉ là những cổ máy sắt vụn nếu
chiếc máy tính khơng được đi kèm với một hệ điều hành. Hệ điều hành giúp
chiếc máy tính trở nên thơng minh hơn, và ngày càng làm được nhiều việc


hơn. Qua đó cho thấy tầm quan trọng khơng thể thiếu của hệ điều hành và yêu
cầu cần phải hiểu được nguyên lý làm việc hệ điều hành trong việc lập trình là
điều hết sức quan trọng.
Sau máy tính, một sản phẩm nữa của lồi người với sự quan trọng
khơng kém trong cuộc sống hiện đại, và luôn đi kèm với chiếc máy tính, đó là
Internet. Internet kết nối nhiều máy tính lại với nhau, bỏ qua bất lợi về mặc
địa lý hay mơi trường. Điều đó đã và đang tạo ra nhiều sự thay đổi ở mọi
trong đời sống kinh tế – xã hội. Hiện nay hầu hết các ứng dụng khơng cịn
chạy độc lập trên máy cục bộ mà đều tương tác với các máy khác qua mạng
internet hoặc tương tác với server. Điều đó cũng có nghĩa là lập trình mạng
ngày càng quan trọng và khơng thể thiếu trong việc lập trình các ứng dụng.
Sau khi hồn thành hai học phần lý thuyết gồm Nguyên lý hệ điều hành
và Lập trình mạng, em đã nghiên cứu và thực hiện đồ án này như là một cách
để kiểm nghiệm lại những lý thuyết đã học. Đồng thời giải quyết những vấn
đề đặt ra từ việc đưa lý thuyết vào trong thực tế. Qua đó góp phần hồn thiện
và củng cố hơn nữa những kiến thức về Nguyên lý hệ điều hành cũng như
Lập trình mạng.
Với 2 mảng đề tài, tương ứng là 2 bài tốn tiêu biểu sẽ được trình bày
trong khuôn khổ Đồ án Cơ sở ngành mạng. Gồm Bài tốn năm triết gia ăn tối
và Tìm hiểu giao thức smtp và pop3, xây dựng mail Client.
Em xin gửi lời cảm ơn chân thành đến Ths. GV Trần Hồ Thủy Tiên đã
nhiệt tình giúp đỡ để giúp em hồn thành đồ án này.
SV. Trần Đại Sơn


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN I: NGUYÊN LÝ HỆ ĐIỀU HÀNH

PHẦN I:


NGUYÊN LÝ HỆ ĐIỀU HÀNH
BÀI TOÁN NĂM TRIẾT GIA ĂN TỐI
CHƯƠNG 1: CƠ SỞ LÝ THUYẾT
1. Mơ tả bài tốn
a. Đề tài và mơ tả vấn đề
5 nhà triết học cùng ngồi ăn tối với món Spaghetti nổi tiếng. Mỗi nhà
triết học cần dùng 2 cái nĩa để có thể ăn Spaghetti. Nhưng trên bàn chỉ có
tổng cộng 5 cái nĩa để xen kẽ giữa 5 nhà triết gia. Mỗi nhà triết học sẽ suy
ngẫm các triết lý của mình đến khi cảm thấy đói thì dự định lần lượt cầm 1 cái
nĩa bên trái và 1 cái nĩa bên phải để ăn. Nếu cả 5 nhà triết học đều cầm cái nĩa
bên trái cùng lúc, thì sẽ khơng có ai có được cái nĩa bên phải để có thể bắt đầu
thưởng thức spaghetti . Đây chính là tình trạng tắc nghẽn.

Hình 1: Bữa ăn tối của các triết gia

b. Yêu cầu bài toán
Yêu cầu đặt ra là phải thiết kế thuật toán sao cho khi một triết gia bị đói
thì ơng ta sẽ được ăn và đảm bảo khơng có triết gia nào bị đói.
Bài toán đặt nên vấn đề đồng bộ giữa các tiến trình để giải quyết các về
đề về tắt nghẽn có thể xảy ra.

Sinh viên: Trần Đại Sơn – Lớp 12T4

3


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN I: NGUYÊN LÝ HỆ ĐIỀU HÀNH


2. Các khái niệm
a. Tiến trình
Tiến trình (proccess) là một chương trình đang xử lý, nó sở hữu một con
trỏ lệnh, tập các thanh ghi và biến, được quản lý qua các thẻ. Để hoàn thành
nhiệm vụ của mình, các tiến trình có thể cịn u cầu một số tài nguyên hệ
thống như CPU, bộ nhớ và các thiết bị ngoại vi.
Tiến trình trong hệ thống có thể chia thành hai loại: tiến trình tuần tự và
tiến trình song song.
Tiến trình tuần tự là các tiến trình mà điểm khởi tạo của nó là điểm kết
thúc của tiến trình trước đó. Tiến trình tuần tự xuất hiện trong các hệ điều
hành đơn nhiệm như MSDOS.
Tiến trình song song là các tiến trình mà điểm khởi tạo của triến trình
này nằm ở thân của các tiến trình khác. Tức là có thể khởi tạo một tiến trình
mới khi các tiến trình trước đó chưa kết thúc. Các tiến trình song xuất hiện
trong các hệ điều hành đa nhiệm được sử dụng phổ biến hiện nay như
Microsoft Windows.
P3
P2
P1

Time
Hình 2: Các tiến trình song song

b. Luồng
Luồng (thread) là một thành phần của tiến trình sở hữu ngăn xếp và thực
thi độc lập ngay trong đoạn mã của tiến trình. Nếu như hệ điều hành có nhiều
tiến trình thì trong mỗi tiến trình bạn có thể tạo ra nhiều tuyến trình hoạt động
song song trong hệ điều hành. Ưu điểm của luồng là chúng hoạt động trong
cùng một không gian địa chỉ của tiến trình. Tập hợp một nhóm các tuyến có

thể sử dụng chung biến tồn cục, vùng nhớ heap, bảng mơ tả file… của tiến
trình, cơ chế liên lạc giữa các luồng đơn giản và hiệu quả hơn cơ chế liên lạc
giữa các tiến trình với nhau. Ngày nay với các bộ vi xử lý đa lõi thì các luồng
thật sự chạy song song chứ không phải giả lập xoay vịng.
Ưu điểm của sử dụng tuyến trong tiến trình đơn giản hơn lập trình tuần
tự. Nhiều thao tác xuất nhập hoặc hiển thị dữ liệu có thể tách rời và phân cho
các tuyến chạy độc lập thực thi. Ví dụ trong môi trường đồ họa, khi copy một
Sinh viên: Trần Đại Sơn – Lớp 12T4

4


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN I: NGUYÊN LÝ HỆ ĐIỀU HÀNH

file có dung lượng lớn, chương trình sẽ được thiết kế để một luồng chỉ đọc dữ
liệu từ đĩa cứng, luồng khác sẽ phụ trách việc hiển thị phần trăm cơng việc
hồn thành lên màn hình cho người dùng theo dõi.
Đối với hệ điều hành chi phí để chuyển đổi giữa ngữ cảnh của tiến trình
cao và chậm hơn chi phí chuyển đổi ngữ cảnh dành cho luồng (với tiến trình
hệ điều hành phải cất thơng số mơi trường, thanh ghi trạng thái, hốn đổi
vùng nhớ,…).
Tuy nhiên, điểm yếu của việc dùng luồng đó là khả năng đổ vỡ (crash)
của một tuyến sẽ ảnh hưởng đến tất cả các tuyến khác và tồn bộ tiến trình
đang hoạt động. Lý do là các luồng dùng chung vùng nhớ và khơng gian địa
chỉ của tiến trình. Ngược lại, một tiến trình bị đổ vỡ ln được hệ điều hành
cơ lập hồn tồn, khơng gây ảnh hưởng đến các tiến trình khác. Tiến trình có
thể chạy trên nhiều máy khách nhau trong khi tuyến chỉ có thể được thực thi
trên một máy và trong cùng một tiến trình.

c. Tài nguyên găng và đoạn găng
Các tài nguyên logic và vật lý phân bổ cho các tiến trình song hành là tài
nguyên “găng”.
Trong môi trường hệ điều hành đa nhiệm – đa chương – đa người dùng,
việc chia sẻ tài nguyên dùng chung giữa các tiến trình là rất cần thiết, nhưng
nếu hệ điều hành không tổ chức tốt việc dùng chung của các tiến trình hoạt
động đồng thời thì khơng những khơng mang lại hiệu quả khai thác tài ngn
mà cịn làm hư hỏng dữ liệu của người dùng. Đó là điều mà cả hệ điều hành
lẫn người lập trình khơng mong muốn.
Các tiến trình hoạt động đồng thời thường cạnh tranh nhau trong việc sử
dụng tài nguyên dùng chung. Hai tiến trình hoạt động đồng thời cùng ghi vào
một vùng chờ chung (dùng chung biến) trên bộ nhớ hay hai tiến trình đồng
thời cùng ghi dữ liệu vào một file chia sẻ, đs là những biểu hiện của sự cạnh
tranh về sử dụng tài nguyên dùng chung giữa các tiến trình. Để các tiến trình
hoạt động, đồng thời khơng xung đột với nhau khi sử dụng tài nguyên dùng
chung, hệ điều hành cần phải tổ chức cho các tiến trình này được độc quyền
đọc - ghi trên các tài nguyên dùng chung này.
Các đoạn mã chương trình sử dụng tài nguyên găng gọi là đọan găng hay
đoạn tới hạn (Critical Section). Tức là các đoạn mã dùng để đọc – ghi các
vùng nhớ chia sẻ, các tập tin chia sẻ được gọi là đoạn găng.

Sinh viên: Trần Đại Sơn – Lớp 12T4

5


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN I: NGUYÊN LÝ HỆ ĐIỀU HÀNH


Để hạn chế lỗi có thể xảy ra do sử dụng tài nguyên găng, hệ điều hành
phải điều khiển các tiến trình sao cho tại mỗi thời điẻm chỉ có một tiến trình
nằm trong đoạn găng. Nếu có nhiều tiến trình muốn vào đoạn găng (thực hiện
đoạn mã) thì chỉ có một tiến trình được vào, các tiến trình cịn lại phải chờ.
Sau khi tiến trình ra khỏi đoạn găng phải báo cho hệ điều hành và/hoặc các
tiến trình khác biết để các tiến trình sau vào đoạn găng.
Các cơng tác điều khiển tiến trình thực hiện đoạn găng của hệ điều hành
được gọi là điều độ tiến trình qua đoạn găng. Để cơng tác điều độ tiến trình
qua đoạn găng được thành cơng, cần phải có sự phối hợp giữa vi xử lý, hệ
điều hành và người lập trình. Vi xử lý đưa ra các chỉ thị, hệ điều hành cung
cấp các công cụ để người lập trình xây dựng các sơ đồ điều độ hợp lý, để đảm
bảo sự độc quyền trong việc sử dụng tài nguyên găng của các tiến trình.
d. Giải pháp Semaphore
Semaphore được đề xuất bởi nhà toán học Dijikstra và năm 1965. Có thể
xem semaphore là một mở rộng của Mutex locks.
Một semaphore s là một biến có các thuộc tính sau:
• Một giá trị nguyên dương e(s).
• Một hàng đợi f(s) lưu danh sách các tiến trình đang bị khóa
(chờ) trên semaphore s.
Chỉ có hai thao tác được định nghĩa trên semaphore
Down(s): giảm giá trị của semaphore s đi 1 đơn vị nếu semaphore có
trị e(s) > 0, và tiếp tục xử lý. Ngược lại, nếu e(s) ≤ 0, tiến trình phải
chờ đến khi e(s) >0.
Up(s): tăng giá trị của semaphore s lên 1 đơn vị. Nếu có một hoặc
nhiều tiến trình đang chờ trên semaphore s, bị khóa bởi thao tác Down, thì hệ
thống sẽ chọn một trong các tiến trình này để kết thúc thao tác Down và cho
tiếp tục xử lý.

Sinh viên: Trần Đại Sơn – Lớp 12T4


6


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG
Semaphor
e

e(s)

S1

0

S2

3

S3

0

PHẦN I: NGUYÊN LÝ HỆ ĐIỀU HÀNH
f(s)
P1

null
P2

P5


Hình 3: Semaphore s

Mỗi tiến trình trước khi vào đoạn găng thì phải gọi Down để kiểm tra và
xác nhận lập quyền vào đoạn găng.
Mỗi tiến trình ngay sau khi ra khỏi đoạn găng phải gọi Up để kiểm tra
xem có tiến trình nào đang đợi trong hàng đợi khơng, nếu có thì đưa tiến trình
trong hàng đợi vào đoạn găng. Khi tiến trình gọi p thì hệ thống sẽ thực hiện.
Cài đặt: Gọi P là tiến trình thực hiện thao tác Down(s) hay Up(s).
Down(s) {

Up(s) {
e(s) = e(s) - 1;

e(s) = e(s) + 1;

if e(s) < 0 {

if s ≤ 0 {

status(P)= blocked;

//Q là tiến trình đang chờ trên s

enter(P,f(s));

exit(Q,f(s));
status (Q) = ready;

}


enter(Q, ready-list);
}

Cài đặt này có thể đưa đến một giá trị âm cho semaphore, khi đó trị tuyệt
đối của semaphore cho biết số tiến trình đang chờ trên semaphore.

Sinh viên: Trần Đại Sơn – Lớp 12T4

7


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN II: LẬP TRÌNH MẠNG

e. Deadlock
i. Định nghĩa
Một tập hợp các tiến trình được định nghĩa ở trong tình trạng tắc nghẽn
khi mỗi tiến trình trong tập hợp đều chờ đợi một sự kiện mà chỉ có một tiến
trình khác trong tập hợp mới có thể phát sinh được.
Nói cách khác, mỗi tiến trình trong tập hợp đều chờ được cấp phát một
tài nguyên hiện đang bị một tiến trình khác cũng ở trạng thái blocked chiếm
giữ. Như vậy khơng có tiến trình nào có thể tiếp tục xử lý , cũng như giải
phóng tài ngun cho tiến trình khác sử dụng, tất cả các tiến trình trong tập
hợp đều bị khóa vĩnh viễn.


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN II: LẬP TRÌNH MẠNG


ii. Điều kiện xuất hiện deadlock
Coffman, Elphick và Shoshani đã đưa ra 4 điều kiện cần có thể làm xuất
hiện tắc nghẽn:
Có sử dụng tài ngun khơng thể chia sẻ (Mutual exclusion): Mỗi thời
điểm, một tài nguyên không thể chia sẻ được hệ thống cấp phát chỉ cho một
tiến trình , khi tiến trình sử dụng xong tài nguyên này, hệ thống mới thu hồi
và cấp phát tài nguyên cho tiến trình khác.
Sự chiếm giữ và yêu cầu thêm tài nguyên (Wait for): Các tiến trình tiếp
tục chiếm giữ các tài nguyên đã cấp phát cho nó trong khi chờ được cấp phát
thêm một số tài nguyên mới.
Không thu hồi tài nguyên từ tiến trình đang giữ chúng (No preemption):
Tài ngun khơng thể được thu hồi từ tiến trình đang chiếm giữ chúng trước
khi tiến trình này sủ dụng chúng xong.
Tồn tại một chu kỳ trong đồ thị cấp phát tài ngun (Circular wait): có ít
nhất hai tiến trình chờ đợi lẫn nhau : tiến trình này chờ được cấp phát tài
nguyên đang bị tiến trình kia chiếm giữ và ngược lại.
Khi có đủ 4 điều kiện này, thì tắc nghẽn xảy ra. Nếu thiếu một trong 4
điều kiện trên thì khơng có tắc nghẽn.
P

R

P

R

P đang giữ R

P đang yêu cầu R


R1
P1

P2

R1

Một tình huống tắt nghẽn
Hình 4: Đồ thị cấp phát tài nguyên




iii. Các phương pháp xử lý tắt nghẽn
Chủ yếu có ba hương tiếp cận để xử lý tắc nghẽn :
Sử dụng một phương thức để bảo đảm rằng hệ thống không bao giờ xảy ra tắc
nghẽn.
Cho phép xảy ra tắc nghẽn và tìm cách sữa chữa tắc nghẽn.


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG


PHẦN II: LẬP TRÌNH MẠNG

Hồn tồn bỏ qua việc xử lý tắc nghẽn, xem như hệ thống không bao giờ xảy
ra tắc nghẽn.
Các phương thức chặn tắc nghẽn đều tập trun giải quyết 4 điều kiện gây
ra tắt nghẽn như đã nêu trên, sao cho hệ thống không thể xảy ra đồng thời 4

điều kiện tắc nghẽn.
3. Ngơn ngữ lập trình Java
a. Giới thiệu
Java là ngơn ngữ lập trình hướng đối tượng, do vậy khơng thể dùng Java
để viết một chương trình hướng chức năng. Java có thể giải quyết hầu hết các
cơng việc mà các ngơn ngữ khác có thể làm được.
Java là ngôn ngữ vừa biên dịch vừa thông dịch. Đầu tiên mã nguồn được
biên dịch bằng công cụ JAVAC để chuyển thành dạng ByteCode. Sau đó được
thực thi trên từng loại máy cụ thể nhờ chương trình thơng dịch. Mục tiêu của
các nhà thiết kế Java là cho phép người lập trình viết chương trình một lần
nhưng có thể chạy trên bất cứ phần cứng cụ thể.
Ngày nay, Java được sử dụng rộng rãi để viết chương trình chạy trên
Internet. Nó là ngơn ngữ lập trình hướng đối tượng độc lập thiết bị, khơng
phụ thuộc vào hệ điều hành. Nó không chỉ dùng để viết các ứng dụng chạy
đơn lẻ hay trong mạng mà cịn để xây dựng các trình điều khiển thiết bị cho
điện thoại di động, PDA.
Đây cũng là ngơn ngữ lập trình được sử dụng để lập trình TCP Socket
phổ biến nhất và được sử dụng để giảng dạy cho mơn học Lập trình mạng.
Đồ án này được viết bằng ngơn ngữ Java với các gói và các lớp được hỗ
trợ sẵn cho việc lập đa tuyến như locks, ReentrantLock, Thread, và các gói hỗ
trợ lập trình giao diện như awt và swing.
b. Đa luồng trong Java
Với cơ chế multithread ứng dụng của ta có thể thực thi nhiều cơng việc
đồng thời. Mỗi thread sẽ có một chỉ số xác định quyền ưu tiên, thread nào có
quyền ưu tiên cao sẽ được thực thi trước, và thấp thì thực thi sau. Việc thực
thi và đánh chỉ số ưu tiên sẽ được thực hiện tự động bởi hệ điều hành để tránh
một thread nào đợi quá lâu mà khơng được vào xử lý.
Trong chương trình java ln có một thread chính được thực thi, các
thread khác sẽ được tạo ra như các thread con, việc mở đầu và kết thúc một
ứng dụng được thực hiện bởi thread chính đó. Để tạo và quản lý thread trong

java, chúng ta dựa vào lớp Thread và Runnable interface.


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN II: LẬP TRÌNH MẠNG

Runnable là một interface dùng để các lớp khác implement để sử dụng
thread. Trong lớp implement đó bắt buộc phải định nghĩa một phương thức là
void run().
Trong kỹ thuật đa luồng, nếu các luồng sử dụng dữ liệu độc lập thì ta
khơng có gì phải tranh luận. Nhưng nếu trên hệ thống nhiều CPU hoặc CPU
đa nhân hay CPU hỗ trợ siêu phân luồng, các luồng sẽ thục sự hoạt động song
song tại cùng một thời điểm. Như vậy, nếu các luồng này cùng truy xuất đến
một biến dữ liệu hoặc một phương thức nhờ vào lý do đã nói ở phần trên, điều
này có thể gây ra việc sai lệch dữ liệu. Ngơn ngữ lập trình Java cung cấp một
cách rất tiện lợi cho việc tạo ra Thread và đồng bộ hóa các nhiệm vụ bằng
cách sử dụng các khối synchronized (đồng bộ). Bạn giữ các nguồn tài nguyên
được chia sẻ trong khối này.
Một khối Synchronized block đánh dấu một phương thức hay một khối
mã là được đồng bộ. Sử dụng khối đồng bộ trong Java có thể tránh xảy ra
xung đột.
Thay vì sử dụng từ khóa synchronized, Concurrency API hỗ trợ lớp
interface Lock dùng chung cho các locks cụ thể. Locks hỗ trợ nhiều phương
thức nhỏ hơn cho việc kiểm soát lock. Trong bộ JDK chuẩn, chúng ta còn
được phép implements nhiều lock.
Lớp ReentrantLock là một lock sử dụng tài nguyên dùng chung không
chia sẻ (mutual exclusion) với các hành vi cơ bản tương tự như chiếm quyền
truy cập monitor qua từ khóa synchronized nhưng được mở rộng thêm các
tính năng.

Ví dụ mẫu dượng đây cho thấy việc sử dụng ReentrantLock hoàn toàn
tương tự như việc sử dụng từ khóa synchronied
ReentrantLock lock = new ReentrantLock();
int count = 0;
void incement() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN II: LẬP TRÌNH MẠNG

Một khóa chiếm quyền truy cập qua phương thức lock() và nhả ra
bằng phương thức unlock(). Đoạn mã phải được đặt bên trong khối
try/catch để đảm bảo trong các trường hợp có ngoại lệ xảy ra. Nếu một thread
khác gọi hàm lock() để chiếm quyền truy cập lock, thì luồng đang chạy sẽ
bị tạm ngưng đến khi lock được thả ra (unlock), điều này đảm bảo chỉ có 1
thread được chiếm giữ lock tại một thời điểm.
Phương thức tryLock() là một phương thức có thể dùng thay thế cho
lock(), khi phương thức này được gọi, nó sẽ cố gắng chiếm quyền truy cập
lock mà không dừng thread đang chạy. Phương thức trả về kiểu boolean để
kiếm tra lock đã thực sự bị chiếm quyền truy cập hay chưa trước khi truy cập
đến bất kỳ một biến tài nguyên dùng chung nào.



BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG




PHẦN II: LẬP TRÌNH MẠNG

CHƯƠNG 2: PHÂN TÍCH VÀ THIẾT KẾ HỆ THỐNG
1. Phân tích chức năng
Chương trình phải mơ phỏng được bài tốn 5 Triết gia ăn tối. Xử lý được tình
trạng tắt nghẽn (deadlock).
Ghi lại log của cả quá trình chạy chương trình, nhằm theo dõi chi tiết thơng
tin về việc sử dụng tài nguyên dùng chung (cầm đũa) của 5 triết gia.
2. Xây dựng các lớp
a. Lớp Chopstick
Lớp Chopstick dùng để mô tả tài nguyên dùng chung ở đây là cái nĩa
(nếu nói là chiếc đũa thì hợp lý hơn).
Lớp Chopstick với đặc điểm là một tài nguyên dùng chung giữa các
triết gia, do đó lớp này được kế thừa từ lớp ReentrantLock. Như đã trình bày
ở mục cơ sở lý thuyết, một đối tượng reentrantLock là một tài nguyên dùng
chung khơng chia sẻ (mutual exclusion).
Một thuộc tính quan trọng của lớp là id. Dùng để đánh dấu chiếc đũa
của triết gia nào.
b. Lớp Philosopher
Lớp Philosopher đại diện cho một triết gia. Một triết gia được mô
phỏng bởi một luồng trong chương trình, do đó lớp Philosopher implements
lớp Runnable để một đối tượng triết gia được tạo ra là một luồng thực thi.
Các thuộc tính bên trong lớp được mơ tả trong bảng dưới:


Thuộc tính
id
leftChopstick
rightChopstick
noMeals

Phương thức
think()
pickUpChopstick(Lock
leftChopstick, Lock
rightChopstick)
eat()
putDownChopsticks()

Philosopher
Kiểu dữ
liệu
int
Lock
Lock
int[]

Kiểu dữ
liệu trả về

Mô tả
Số thứ tự
Chiếc đũa bên trái
Chiếc đũa bên phải
Số bữa ăn đã ăn


Mô tả

void
void

Suy nghĩ
Cầm đũa lên

void
void

Ăn
Thả đũa xuống


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN II: LẬP TRÌNH MẠNG

Các cơng việc mà một đối tượng Philosopher phải thực hiện sẽ được
đặt trong phương thức run() đó là làm được override lại từ lớp Runnable.
Một triết gia sẽ thực hiện lần lượt các hành động, và theo đó các phương thức
sau được gọi lần lượt: think(), pickUpChopstick(leftChopstick,
rightChopstick), eat(), putDownChopsticks();

Để bắt các biệt lệ có thể xảy ra trong quá trình thực thi, các phương
thức trên sẽ được gọi trong khôi try/catch.
public void run() {
try {

while (!isStop) {
try {
// check if the thread should wait first,
// if not let it do a full iteration of the loop
if (waitRequested) {
synchronized (this) {
wait();
// resuming here so wait wouldn't be requested anymore
waitRequested = false;
}
}
//do whatever a philosopher does
think();
pickUpChopstick(leftChopstick, rightChopstick);

eat();
putDownChopsticks();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
System.out.println("Philosopher " + id + " was interrupted.\n");
}
}


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN II: LẬP TRÌNH MẠNG


Phương thức think() thể hiện triết gia đang suy nghĩ, thực chất ở đây
sẽ gọi phương thức tĩnh sleep() của lớp Thread, sleep trong một khoảng thời
gian được tự động tạo ra ngẫu nhiên. Phương thức sẽ phóng ra một
InterruptedException khi xảy ra biệt lệ.
private void think() throws InterruptedException {
System.out.println("Philosopher " + id + " is thinking.\n");
System.out.flush();
if (id == 0) {
state[0] = 4;
}
if (id == 1) {
state[1] = 4;
}
if (id == 2) {
state[2] = 4;
}
if (id == 3) {
state[3] = 4;
}
if (id == 4) {
state[4] = 4;
}//
Thread.sleep(1000 + timeGenerator.nextInt(1000));
}

Phương thức pickUpChopstick() đại diện cho hành động cầm đũa lên.
Tham số truyền vào là 2 Lock, đại diện cho 2 chiếc đũa bên cạnh triết gia
(đũa trái và đũa phải). Phương thức tryLock() được gọi trên 2 đối tượng
được truyền vào, nếu 2 đũa đều đang "rãnh" thì thốt khỏi vịng lặp để tiếp tục

"việc ăn". Nếu có 1 trong 2 chiếc hoặc cả 2 chiếc đũa đang bị triết gia bên
cạnh cầm lên thì gọi phương thức unlock() để đặt đũa xuống lại. Luồng tạm
dừng trong 1 giây, sau đó lại thực hiện cầm đũa lên với phương thức
tryLock() như trên. Như vậy sẽ tránh được trường hợp tắt nghẽn khi mà cả 5
triết gia đều cầm đũa lên và chờ đợi chiếc đũa từ người bên cạnh nhưng
không có ai thả đũa xuống.


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN II: LẬP TRÌNH MẠNG

private void pickUpChopstick(Lock leftChopstick, Lock
rightChopstick) {
while (true) {
boolean lc = false;
boolean rc = false;
try {
// Cầm đũa lên
lc = leftChopstick.tryLock();
rc = rightChopstick.tryLock();
} finally {
if (lc && rc) {
// Nếu cả 2 đũa có sẵn
System.out.println("Philosopher " + id
Pick Up both Chop Stick.\n");
quote[id] = 3;
// Thốt vịng lặp để tiếp tục việc ăn
return;
}

// Nếu chỉ cầm được đũa trái
if (lc) {
System.out.println("Philosopher " + id
Pick Up leftChop Stick And RightChop Stick is Busy.\n");
// Đặt đũa trái xuống lại
leftChopstick.unlock();
System.out.println("Philosopher " + id
Put Down leftChop Stick.\n");
quote[id] = 1;
}
// Nếu chỉ cầm được đũa phải
if (rc) {
System.out.println("Philosopher " + id
Pick Up RightChop Stick And lefttChop Stick is Busy.\n");
// Đặt đũa phải xuống lại
rightChopstick.unlock();
System.out.println("Philosopher " + id
Put Down RightChop Stick.\n");
quote[id] = 2;
}
}
try {
Thread.sleep(1000);

+ "

+ "

+ "


+ "

+ "


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN II: LẬP TRÌNH MẠNG

} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

Phương thức eat(), đại diện cho hành động ăn của triết gia. Phương
thức sẽ được thực hiện sau khi triết gia lấy được cả 2 chiếc đũa. Phương thức
này thực chất sẽ gọi phương thức tĩnh sleep() của lớp Thread, sau đó tăng
thêm 1 vào giá trị của thuộc tính noMeals của triết gia tương ứng.
private void eat() throws InterruptedException {
// Tạm dừng luồng 50ms
Thread.sleep(50);
quote[id] = 0;
// Tăng biến đếm số bữa ăn lên
increment(id);
System.out.println("Philosopher " + id + " is eating.\n");
// Nếu là triết gia 0 vừa ăn
if (id == 0) {
chopstick.setAxis(0, 340, 350);
chopstick.setAxis(4, 380, 350);

state[0] = 0;
}
// Nếu là triết gia 1 vừa ăn
if (id == 1) {
chopstick.setAxis(0, 220, 290);
chopstick.setAxis(1, 200, 250);
state[1] = 0;
}
// Nếu là triết gia 2 vừa ăn
if (id == 2) {
chopstick.setAxis(1, 240, 150);
chopstick.setAxis(2, 280, 120);
state[2] = 0;
}
// Nếu là triết gia 3 vừa ăn
if (id == 3) {
chopstick.setAxis(2, 430, 120);
chopstick.setAxis(3, 500, 150);


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN II: LẬP TRÌNH MẠNG

state[3] = 0;
}
// Nếu là triết gia 4 vừa ăn
if (id == 4) {
chopstick.setAxis(3, 530, 270);
chopstick.setAxis(4, 490, 315);

state[4] = 0;
}
System.out.flush();
Thread.sleep(400);
if (id == 0) {
state[0] = 1;
}
if (id == 1) {
state[1] = 1;
}
if (id == 2) {
state[2] = 1;
}
if (id == 3) {
state[3] = 1;
}
if (id == 4) {
state[4] = 1;
}
Thread.sleep(400);
if (id == 0) {
state[0] = 2;
}
if (id == 1) {
state[1] = 2;
}
if (id == 2) {
state[2] = 2;
}
if (id == 3) {

state[3] = 2;
}
if (id == 4) {
state[4] = 2;
}


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN II: LẬP TRÌNH MẠNG

Thread.sleep(400);
if (id == 0) {
state[0] = 3;
}
if (id == 1) {
state[1] = 3;
}
if (id == 2) {
state[2] = 3;
}
if (id == 3) {
state[3] = 3;
}
if (id == 4) {
state[4] = 3;
}
Thread.sleep(200 + timeGenerator.nextInt(200));
}


Phương thức putDownChopsticks(), đại diện cho hành động đặt đũa
xuống của triết gia. Phương thức này được triệu gọi sau khi đã thực hiện xong
phương thức eat(). Thực chất chỉ là gọi phương thức unlock() để thả đũa
trái và đũa phải xuống.
private void putDownChopsticks() {
// Thả đũa trái và phải xuống
leftChopstick.unlock();
rightChopstick.unlock();
System.out.println("Philosopher " + id + " put down Both
ChopStick\n");
if (id == 0) {
chopstick.setAxis(0, 270, 330);
chopstick.setAxis(4, 430, 330);
state[0] = 4;
}
if (id == 1) {
chopstick.setAxis(0, 270, 330);
chopstick.setAxis(1, 200, 200);


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN II: LẬP TRÌNH MẠNG

state[1] = 4;
}
if (id == 2) {
chopstick.setAxis(1, 200, 200);
chopstick.setAxis(2, 350, 120);
state[2] = 4;

}
if (id == 3) {
chopstick.setAxis(2, 350, 120);
chopstick.setAxis(3, 500, 210);
state[3] = 4;
}
if (id == 4) {
chopstick.setAxis(3, 500, 210);
chopstick.setAxis(4, 430, 330);
state[4] = 4;
}
}

c. Lớp App
Lớp App với nhiệm vụ khởi tạo giao diện cho chương trình mơ phỏng,
đồng thời khởi tạo 5 đối tượng triết gia và 5 chiếc đũa qua 2 phương thức
initializePos() và initializePhilosopher(), sau đó khởi chạy 5 luồng
triết gia vừa tạo.
public void initializePos() {
chopstick = new Chopstick[5];
chopstick[0] = new Chopstick(0,
chopstick[1] = new Chopstick(1,
chopstick[2] = new Chopstick(2,
chopstick[3] = new Chopstick(3,
chopstick[4] = new Chopstick(4,
}

270,
200,
350,

500,
430,

public void initializePhilosopher() {
philosophers = new Philosopher[5];

330);
200);
120);
210);
330);


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

philosophers[0]
chopstick[4]);
philosophers[1]
chopstick[0]);
philosophers[2]
chopstick[1]);
philosophers[3]
chopstick[2]);
philosophers[4]
chopstick[3]);
}

PHẦN II: LẬP TRÌNH MẠNG

= new Philosopher(0, chopstick[0],

= new Philosopher(1, chopstick[1],
= new Philosopher(2, chopstick[2],
= new Philosopher(3, chopstick[3],
= new Philosopher(4, chopstick[4],


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN II: LẬP TRÌNH MẠNG

CHƯƠNG 3: TRIỂN KHAI VÀ ĐÁNH GIÁ KẾT QUẢ
1. Lựa chọn công cụ phát triển
Sử dụng phần mềm NetBeans để xây dựng ứng dụng. NetBeans là một
công cụ miễn phí nhưng vơ cùng mạnh mẽ, hỗ trợ đầy đủ ngơn ngữ lập trình
Java với giao diện dễ sử dụng, hỗ trợ thiết kế giao diện đẹp mắt và nhanh
chóng.
2. Chương trình hồn chỉnh

Hình 5: Giao diện chương trình khi mới khởi chạy

Ở giữa chương trình là một bàn ăn, với hình ảnh mơ phỏng 5 triết gia
ngồi quanh một chiếc bàn trịn, trên bàn có 5 đĩa Spaghetti và 5 tương ứng có
chiếc nĩa (tượng trưng cho 5 chiếc đũa như đã trình bày trong bài).
Bên phải là hình ảnh minh họa trạng thái ăn hoặc suy nghĩ (eating hoặc
thinking) của triết gia có số thứ tự tương ứng. Và bên cạnh là thông tin tổng
số lần ăn của mỗi triết gia (No of meals).
Từ giao diện này, người dùng nhấn nút Run ở bên dưới chương trình để
bắt đầu q trình mơ phỏng.



BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN II: LẬP TRÌNH MẠNG

Hình 6: Giao diện chương trình đang chạy

Khi triết gia cầm nĩa lên, chiếc nĩa sẽ thay đổi vị trí, nằm lên trên đĩa,
nếu cầm đũa thành cơng hình ảnh ở cột bên phải sẽ đổi sang trạng thái ứng
với hình ảnh eating. Sau khi ăn xong, số bữa ăn tăng lên, triết gia đặt nĩa
xuống và trở về lại trạng thái thinking.

Hình 7: Giao diện chương trình khi tạm ngưng


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG

PHẦN II: LẬP TRÌNH MẠNG

Hình 8: Log ghi lại tồn bộ q trình chạy

Tịa bộ q trình cầm đũa lên, đặt đũa xuống hay “dùng bữa”, tất cả đều
được ghi lại và in lên màn hình console để tiện theo dõi.


BÁO CÁO ĐỒ ÁN CƠ SỞ NGÀNH MẠNG






PHẦN II: LẬP TRÌNH MẠNG

KẾT LUẬN VÀ HƯỚNG PHÁT TRIỂN
1. Kết quả đạt được
Nắm được lý thuyết đồng bộ hóa giữa các tiến trình.
Giải quyết được vấn đề đồng bộ hóa giữa các tiến trình sử dụng tài nguyên
dùng chung, tránh được hiện tượng deadlock.
Ứng dụng mơ mỏng có giao diện thân thiện, hình ảnh minh họa sinh động, thể
hiện được bề nổi của vấn đề.
2. Hướng phát triển
Kỹ thuật xử lý về đồng bộ hóa giữa các tiến trình có thể được áp dụng
trong hầu hết các chương trình máy tính hiện nay với những hệ thống xử lý
song song truy cập sử dụng tài nguyên dùng chung.


×