You are on page 1of 111

TRƯỜNG ĐIỆN - ĐIỆN TỬ

School of Electrical and Electronics

LẬP TRÌNH NÂNG CAO

ĐA LUỒNG

5/17/2023 Lập trình nâng cao 1


Nội dung

1 Một số khái niệm

2 Lớp Thread

3 Đồng bộ giữa các Thread

4 Giao tiếp giữa các Thread

5 Lập trình song song

Lập trình nâng cao 2


01 Một số khái niệm

Lập trình nâng cao 3


Một số khái niệm

❖Đa luồng (Mulltithread) cho phép thực hiện đồng thời


nhiều tác vụ trong một chương trình.
❖Đa luồng là một trong những tính năng mạnh mẽ của
JAVA.

5/17/2023 Lập trình nâng cao 4


Khái niệm

❖Tác vụ (task): Một đơn vị thực thi, một đơn vị công


việc, một nhiệm vụ cần được thực hiện.
❖Luồng (Thread): Một luồng thực thi từ đầu đến cuối
của một tác vụ.
❖Đồng thời (Concurrently): Máy tính đơn lõi, tác vụ
được chia nhỏ thành nhiều phần, hệ điều hành thực
thi một phần nhỏ của tác vụ rồi nhanh chóng chuyển
sang thực thi một phần nhỏ khác của tác vụ khác.
❖Song song (Parrallel): Máy tính đa lõi, các tác vụ có
thể được xử lý song song trên nhiều lõi.

5/17/2023 Lập trình nâng cao 5


Ưu nhược điểm của lập trình đa luồng

❖Đa luồng có thể làm cho chương trình phản hồi và


tương tác tốt hơn, nâng cao hiệu suất.
▪ Ví dụ, một chương trình soạn thảo tốt nếu nó cho phép đồng
thời vừa có thể in, lưu file và nhập văn bản.
▪ Các ứng dụng đơn luồng có thể dẫn đến khả năng phản hồi
kém, do các hoạt động kéo dài trước khi các hoạt động khác
được bắt đầu.
▪ Chương trình đa luồng chạy nhanh hơn các chương trình đơn
luồng ngay cả trên các hệ thống xử lý đơn nhân.
• Ví dụ, khi một luồng không thể tiếp tục do nó đang chờ đợi kết quả
của hoạt động I/O, luồng khác có thể sử dụng bộ vi xử lý.
❖Lập trình, kiểm thử, gỡ lỗi các chương trình đa luồng
khó khăn hơn nhiều so với đơn luồng, rất dễ xảy ra
lỗi.

5/17/2023 Lập trình nâng cao 6


Ví dụ ứng dụng đa luồng

❖Khi phát audio/video qua internet, người dùng không


muốn đợi đến khi tải xuống toàn bộ audio hoặc video
trước khi bắt đầu phát lại.
❖Giải pháp: có thể sử dụng nhiều luồng – một luồng để
tải audio/video và một luồng khác để phát
audio/video đã được tải về. Các hoạt động này được
tiến hành đồng thời. Để việc phát lại được mượt thì
các luồng tải và luồng phát phải được đồng bộ hóa,
nghĩa là các hành động của chúng được phối hợp với
nhau, để luồng phát không bắt đầu cho đến khi luồng
tải đã tải đủ lượng audio/video trong bộ nhớ.

5/17/2023 Lập trình nâng cao 7


02 Thread

Lập trình nâng cao 8


Trạng thái và vòng đời của thread

5/17/2023 Lập trình nâng cao 9


Trạng thái và vòng đời của thread

❖Trạng thái new và runnable:


▪ Khi được tạo ra, thread mới bắt đầu vòng đời của nó ở trạng
thái new.
▪ Khi thread được bắt đầu, nó sẽ chuyển sang trạng thái
runnable – trạng thái đang thực thi nhiệm vụ của tác vụ.
❖Trạng thái waiting:
▪ Thread chuyển sang trạng thái waiting khi nó đợi một thread
khác.
▪ Trạng thái waiting chỉ có thể chuyển về trạng thái runnable.
▪ Khi thread ở trạng thái waiting, thread không sử dụng CPU.
❖Trạng thái Timed waiting:
▪ Thread có thể chuyển sang timed waiting trong một khoảng
thời gian nhất định. Khi hết khoảng thời gian đó, thread chuyển
về trạng thái runnable.
▪ Khi thread ở trạng thái timed waiting, thread không sử dụng
CPU.
5/17/2023 Lập trình nâng cao 10
Trạng thái và vòng đời của thread

❖Trạng thái blocked:


▪ Thread có thể chuyển sang trạng thái blocked khi nó cố gắng
thực hiện một tác vụ không thể hoàn thành ngay lập tức và nó
phải tạm thời đợi cho đến khi tác vụ đó hoàn thành.
▪ Ví dụ: khi thread đưa ra một yêu cầu đọc/ghi (I/O), hệ điều
hành sẽ chặn thread thực thi cho đến khi yêu cầu I/O đó hoàn
thành.
▪ Khi thread ở trạng thái blocked, thread không sử dụng CPU.
❖Trạng thái terminated:
▪ Thread sẽ chuyển sang trạng thái terminated (trạng thái kết
thúc hay còn gọi là trạng thái chết) khi nó hoàn thành công
việc, hoặc bị lỗi.

5/17/2023 Lập trình nâng cao 11


Trạng thái và vòng đời của thread

❖ Ở góc độ hệ điều hành, trạng thái runnable của Java


gồm hai trạng thái riêng biệt: ready và running.
▪ Hệ điều hành ẩn các trạng thái này khỏi máy ảo Java (JVM), chỉ
nhìn thấy trạng thái runnable.
▪ Khi thread chuyển sang trạng thái runnable từ trạng thái new, thì
hệ điều hành xem như là trạng thái ready.
▪ Thread chuyển từ trạng thái ready sang trạng thái running khi hệ
điều hành giao cho nó một bộ vi xử lý – còn gọi là điều phối
luồng.
▪ Trong hầu hết các hệ điều hành, mỗi thread được cung cấp một
lượng thời gian nhỏ của bộ xử lý – được gọi là lượng tử (quantum)
hoặc lát thời gian (timeslice) – để thực hiện nhiệm vụ của nó.

5/17/2023 Lập trình nâng cao 12


Trạng thái và vòng đời của thread
❖Khi lượng tử đó hết hạn, thread sẽ trở lại trạng thái ready
và hệ điều hành sẽ gán một thread khác cho bộ vi xử lý.
Việc chuyển đổi giữa các trạng thái ready và running chỉ do
hệ điều hành xử lý. JVM không “nhìn thấy” các quá trình
chuyển đổi – nó chỉ xem thread là runnable và giao nó cho
hệ điều hành để chuyển đổi thread giữa ready và running.
❖Quá trình hệ điều hành sử dụng để xác định thread nào để
thực thi được gọi là lập lịch luồng (thread scheduling) và
phụ thuộc vào mức độ ưu tiên của thread.

5/17/2023 Lập trình nâng cao 13


Mức ưu tiên và lập lịch

❖Mỗi thread đều có mức độ ưu tiên (priority) giúp xác


định thứ tự mà các thread được thực thi.
❖Mỗi thread mới kế thừa mức ưu tiên của thread đã tạo
ra nó.
❖Các thread có mức ưu tiên cao hơn, quan trọng hơn,
thì sẽ được CPU xử lý trước.
❖Nhờ cơ chế lập lịch timeslice, các thread có mức ưu
tiên bằng nhau sẽ được thực hiện đồng thời:
▪ Ngay cả khi một thread chưa hoàn thành nhiệm vụ của nó
mà lượng tử thời gian của nó đến hạn thì CPU sẽ dừng thực
hiện thread đó và chuyển sang thực hiện thread tiếp theo.
▪ Nếu không có cơ chế lập lịch timeslice, mỗi thread trong tập
hợp các thread có mức ưu tiên ngang nhau sẽ chạy đến khi
hoàn thành, trước khi thread khác được thực hiện.

5/17/2023 Lập trình nâng cao 14


Mức ưu tiên và lập lịch

❖Khi một thread có mức ưu tiên cao hơn đi vào trạng


thái ready, hệ điều hành thường ưu tiên thread hiện
đang chạy (hoạt động này được gọi là lập lịch trước).
❖Các thread có mức độ ưu tiên cao hơn có thể trì hoãn
(có thể là vô thời hạn) việc thực thi các luồng có mức
ưu tiên thấp hơn.
❖Hệ điều hành sử dụng kỹ thuật lão hóa (aging) để
ngăn chặn tình trạng trì hoãn vô thời hạn:
▪ Khi một thread chờ ở trạng thái ready, hệ điều hành dần dần
tăng mức ưu tiên của thread để đảm bảo rằng cuối cùng
thread đó sẽ được thực thi.

5/17/2023 Lập trình nâng cao 15


03 Lớp Thread

Lập trình nâng cao 16


Tạo Task và Thread

❖Một tác vụ (task) là một object.


❖Định nghĩa lớp tác vụ
▪ Triển khai giao diện Runnable.
▪ Định nghĩa phương thức run() của Runnable
❖Tác vụ phải được chạy từ một thread.

5/17/2023 Lập trình nâng cao 17


Tạo Task và Thread

❖Tác vụ được tạo ra bằng cách gọi hàm tạo của lớp tác
vụ (TaskClasss).
TaskClass task = new TaskClass(…);
❖Tác vụ phải được thực thi trong một thread.
▪ Lớp Thread có các hàm tạo để tạo ra các thread và nhiều
phương thức để điều khiển thread
▪ Tạo thread cho tác vụ:
Thread thread = new Thread(task);
▪ Gọi phương thức start() để báo cho JVM biết thread đã sẵn
sàng để chạy.
thread.start();
▪ JVM sẽ thực thi tác vụ bằng cách gọi phương thức run() của
tác vụ.
❖Khi một thread đã kết thúc thì sẽ không thể sử dụng
lại.
5/17/2023 Lập trình nâng cao 18
Ví dụ Task và Thread

❖ Ví dụ minh họa một chương trình có 3 tác vụ và 3


thread để chạy 3 tác vụ:
▪ Tác vụ 1 hiển thị ký tự a 100 lần
▪ Tác vụ 2 hiển thị ký tự b 100 lần
▪ Tác vụ 3 hiển thị số nguyên từ 1 đến 100
❖ Ví dụ kết quả chạy chương trình:

5/17/2023 Lập trình nâng cao 19


Ví dụ Task và Thread

❖Mã nguồn cho lớp tác vụ in ký tự ra màn hình

5/17/2023 Lập trình nâng cao 20


Ví dụ Task và Thread

❖Mã nguồn cho lớp tác vụ in số nguyên ra màn hình

5/17/2023 Lập trình nâng cao 21


Ví dụ Task và Thread

❖Mã nguồn minh họa sử dụng

5/17/2023 Lập trình nâng cao 22


Ví dụ Task và Thread

❖ Kết quả chạy chương trình:


▪ Lần 1

▪ Lần 2

5/17/2023 Lập trình nâng cao 23


Lớp Thread
❖ Lớp Thread cung cấp:
▪ Các hàm tạo cho phép tạo ra các thread để thực thi tác vụ
▪ Các phương thức để điều khiển thread
❖ Lớp Thread triển khai giao diện Runnable

5/17/2023 Lập trình nâng cao 24


Lớp Thread

❖Hàm tạo Thread(): Tạo một thread rỗng.


❖Hàm tạo Thread(task: Runnable): Tạo một thread
cho một tác vụ được chỉ định.
❖start(): Chạy thread, báo cho JVM biết thread đã sẵn
sàng, JVM sẽ thực thi phương thức run() của tác vụ.
❖isAlive(): Kiểm tra xem thread còn đang chạy hay
không.
❖setPriofity(p: int): Đặt mức ưu tiên cho thread. Thứ
tự ưu tiên nằm trong khoảng [1, 10].

5/17/2023 Lập trình nâng cao 25


Lớp Thread

❖sleep(millis: long): Đưa thread vào trạng thái sleep


(trạng thái timed waiting) trong một khoảng mili-giây.
❖yield(): Dừng tạm thời thread, cho phép các thread
khác được chạy
❖join(): Đợi cho đến khi thread này kết thúc
❖interrupt(): Ngắt thread

5/17/2023 Lập trình nâng cao 26


Lớp Thread
❖Định nghĩa lớp tác vụ dẫn xuất từ lớp Thread
▪ Có thể định nghĩa một lớp dẫn xuất từ lớp Thread và thực thi
lại phương thức run(). Sau đó tạo một đối tượng từ lớp này và
gọi phương thức start() để chạy thread.

▪ Khuyến nghị: không nên sử dụng phương pháp này, vì lẫn


lộn giữa tác vụ và cơ chế chạy tác vụ.
5/17/2023 Lập trình nâng cao 27
Lớp Thread
❖Ví dụ sử dụng phương thức yield() để tạm dừng
thread

5/17/2023 Lập trình nâng cao 28


Lớp Thread

❖Ví dụ sử dụng phương thức sleep()

Chạy chương trình và quan sát tốc độ hiển thị


5/17/2023 Lập trình nâng cao 29
Lớp Thread

5/17/2023 Lập trình nâng cao 30


Lớp Thread

❖Ví dụ phương thức join():

❖Các số từ 51 đến 100 được in ra màn hình sau khi


thread4 đã kết thúc.

5/17/2023 Lập trình nâng cao 31


Lớp Thread

❖Mỗi thread trong Java có một mức ưu tiên.


❖Mặc định, một thread sẽ kế thừa mức ưu tiên của
thread tạo ra nó.
❖Có thể thay đổi mức ưu tiên của bất kỳ thread nào
bằng cách sử dụng phương thức setPriority().
❖Mức độ ưu tiên được đánh số từ 1 đến 10. Lớp Thread
định nghĩa các hằng số:
▪ MIN_PRIORITY (1)
▪ NORM_PRIORITY (5)
▪ MAX_PRIORITY (10)
❖Mức ưu tiên mặc định là NORM_PRIORITY
▪ Thread đầu tiên được tạo ra, nếu không thiết lập mức ưu tiên thì
thread được gán mức ưu tiên mặc định
▪ Các thread được tạo ra từ thread khác thì sẽ kế thừa mức ưu tiên của
thread đó. Có thể thay đổi lại mức ưu tiên của thread.
5/17/2023 Lập trình nâng cao 32
Lớp Thread
❖ Ví dụ thay đổi mức ưu tiên cho thread

5/17/2023 Lập trình nâng cao 33


04 Thread Pool

Lập trình nâng cao 34


Thread Pool

❖Phương pháp tạo tác vụ:


▪ triển khai giao diện Runnable và tạo thread để thực thi tác vụ
-> thuận tiện cho việc thực thi một tác vụ đơn lẻ
▪ không hiệu quả đối với một số lượng lớn các tác vụ vì phải tạo
một thread cho mỗi tác vụ.
▪ chạy một thread mới cho mỗi tác vụ có thể hạn chế thông
lượng và gây ra hiệu suất kém.
❖Sử dụng Thread pool để nâng cao hiệu quả
❖Java cung cấp:
▪ Giao diện Executor để thực thi các tác vụ trong một nhóm
thread
▪ Giao diện ExecutorService để quản lý và kiểm soát các tác
vụ.

5/17/2023 Lập trình nâng cao 35


Thread Pool
❖Executor:
▪ Execute(obj: Runnable): thực
hiện tác vụ
❖ExecutorService:
▪ shutdown(): Tắt executor,
nhưng chờ tất cả các tác vụ hoàn
thành.
▪ shutdownNow(): Tắt executor
ngay lập tức, thậm chí vẫn còn
tác vụ đang được thực hiện, trả
về một danh sách các tác vụ đang
thực hiện.
▪ isShutdown(): Trả về true nếu
executor đã tắt
▪ isTerminated(): Trả về true nếu
tất cả các tác vụ trong pool đã
kết thúc.
5/17/2023 Lập trình nâng cao 36
Thread Pool

❖Sử dụng các phương thức static trong lớp Executor để


tạo thread pool.
❖Nếu một thread hoàn thành việc thực thi một tác vụ,
nó có thể được sử dụng lại để thực thi một tác vụ
khác.
❖Nếu một thread kết thúc do lỗi trước khi shutdown,
một thread mới sẽ được tạo để thay thế.

5/17/2023 Lập trình nâng cao 37


Thread Pool

❖newFixedThreadPool
▪ Tạo ra một pool chỉ định rõ số lượng tối đa các thread có thể
chạy đồng thời.
▪ Một thread có thể được sử dụng lại để chạy tác vụ khác sau khi
hoàn thành tác vụ hiện tại.
❖newCachedThreadPool
▪ Tạo một pool có thể tạo các thread mới khi cần
▪ Sử dụng lại các thread đã được tạo trước đó khi các thread đó
đã hoàn thành tác vụ nó thực hiện.

5/17/2023 Lập trình nâng cao 38


Thread Pool
❖ Ví dụ sử dụng Thread pool để thực hiện 3 thread trong ví dụ
trước.

5/17/2023 Lập trình nâng cao 39


Thread Pool

❖Ví dụ sử dụng Thread pool chỉ cho phép tối đa 1


thread chạy tại một thời điểm

5/17/2023 Lập trình nâng cao 40


Thread Pool
❖Ví dụ sử dụng newCachedThreadPool

5/17/2023 Lập trình nâng cao 41


Thread
05 Synchronization
synchronized

Lập trình nâng cao 42


Ví dụ Thread không được đồng bộ
❖Xây dựng một chương trình có:
▪ Lớp Account để tạo ra đối tượng account, có số dư là balance; phương
thức deposit để cộng số dư tài khoản với một lượng amount được
thêm vào; phương thức getBalance trả về số dư hiện tại.
▪ Lớp AddAPennyTask triển khai giao diện Runnable, định nghĩa phương
thức run() của Runnable, có nhiệm vụ mỗi lần gọi thì sẽ tăng số dư
của tài khoản lên 1 đồng.
▪ Lớp AccountWithoutSync: Khởi tạo 100 thread, mỗi thread sẽ thêm 1
đồng vào tài khoản.

❖ Câu hỏi: sau khi 100 thread kết thúc, số dư tài khoản là bao
nhiêu sau?
5/17/2023 Lập trình nâng cao 43
Ví dụ Thread không được đồng bộ

❖ Mã nguồn minh họa

5/17/2023 Lập trình nâng cao 44


Ví dụ Thread không được đồng bộ

❖Kết quả chạy chương trình


▪ Lần 1 Kết luận:
- Số dư tài khoản không phải
100 đồng.
▪ Lần 2 - Mỗi lần chạy cho ra một kết
quả khác nhau
Câu hỏi:
▪ Lần 3 - Hãy giải thích nguyên nhân?

5/17/2023 Lập trình nâng cao 45


Race Condition

❖Hiện tượng chạy đua (race condition)


❖Nguyên nhân: Các thread đồng thời truy nhập vào
cùng một “vùng nhớ chia sẻ”, vùng nhớ quan trọng,
mà không được bảo vệ - không được kiểm soát.

Thread 1 Thread 2 Thread N

Shared resource
(critical region)

5/17/2023 Lập trình nâng cao 47


Race Condition

❖Xem xét lại ví dụ trước

balance là vùng nhớ được chia sẻ


giữa các thread, “critical region”
5/17/2023 Lập trình nâng cao 48
Đồng bộ hóa toàn bộ đối tượng

❖ Sử dụng từ khóa synchronized

5/17/2023 Lập trình nâng cao 49


Đồng bộ hóa toàn bộ đối tượng

❖Khi Task 1 truy nhập vào phương thức deposit, thì


Task 2 sẽ bị chặn lại cho đến khi Task 1 kết thúc
phương thức

5/17/2023 Lập trình nâng cao 50


Đồng bộ hóa khối lệnh

❖ Chỉ đồng bộ hóa một khối lệnh của một phương thức

❖ Áp dụng cho ví dụ trên

❖ Biểu thức expr phải là


một tham chiếu đối tượng

5/17/2023 Lập trình nâng cao 52


Đồng bộ hóa khối lệnh

❖Phương thức đồng bộ hóa có thể được chuyển đổi


thành đồng bộ hóa khối lệnh.

❖Hai cách viết trên là tương đương

5/17/2023 Lập trình nâng cao 53


Các phương án sử dụng synchronized

❖Câu hỏi: phương án nào cho kết quả đúng? Tại sao?

1 4

2
5

3 6

5/17/2023 Lập trình nâng cao 54


Tóm tắt về synchronized

❖Phương thức được đồng bộ (synchronized instance


method): Khóa toàn bộ thể hiện (instance)
❖Đồng bộ hóa khối lệnh (synchronized statements):
Khóa khối lệnh.
▪ Biểu thức expr trong câu lệnh synchronized(expr) để đảm bảo
rằng vùng nhớ quan trọng (critical region) được bảo vệ.
▪ Biểu thức không chính xác thì có thể vùng nhớ quan trọng sẽ
không được bảo vệ
❖Yêu cầu khóa được thực hiện ngầm

5/17/2023 Lập trình nâng cao 55


Đồng bộ hóa - Sử dụng Lock

❖ Giao diện Lock


▪ lock(): Yêu cầu khóa
▪ unlock(): Giải phóng khóa
▪ newCondition(): Trả về một Condition
instance mới được liên kết với Lock
instance này

❖ReentrantLock: Là một triển khai cụ thể của giao


diện Lock
▪ ReentrantLock(): Tương tự như ReentrantLock(false)
▪ ReentrantLock(fair: boolean): Tạo một lock với chính sách
công bằng được chỉ định thông qua tham biến đầu vào.
• TRUE: Thread chờ đợi lâu nhất sẽ nhận được khóa
• FALSE: Cấp khóa cho một thread ngẫu nhiên

5/17/2023 Lập trình nâng cao 56


Đồng bộ hóa - Sử dụng Lock

5/17/2023 Lập trình nâng cao 57


Đồng bộ hóa - Sử dụng Lock

❖ Kết quả chạy chương trình:

5/17/2023 Lập trình nâng cao 58


Thread
06 Communication

Lock & Condition

Lập trình nâng cao 59


Phối hợp giữa các thread

❖Ví dụ về nhu cầu giao tiếp giữa các thread:


▪ Giả sử có hai tác vụ được thực hiện bởi hai thread:
• Một tác vụ thực hiện gửi tiền vào tài khoản,
• Một tác vụ thực hiện rút tiền từ cùng một tài khoản.
• Tác vụ rút tiền phải chờ nếu số tiền được rút nhiều hơn số
dư hiện tại.
• Bất cứ khi nào tiền mới được gửi vào tài khoản, tác vụ gửi
tiền sẽ thông báo cho tác vụ rút tiền được tiếp tục.
• Nếu số tiền vẫn không đủ để rút, tác vụ rút tiền phải tiếp tục
chờ.

5/17/2023 Lập trình nâng cao 60


Phối hợp giữa các thread

❖Giải pháp: Sử dụng lớp Condition và Lock để giao tiếp


giữa các thread.
▪ Sử dụng các phương thức await(), signal() và signalAll() của
Condition để giao tiếp giữa các thread.
• await(): Buộc thread hiện tại đợi cho đến khi condition được báo
hiệu (signal)
• signal(): Đánh thức một thread đang chờ
• signalAll(): Đánh thức tất cả các thread đang chờ

5/17/2023 Lập trình nâng cao 61


Phối hợp giữa các thread

❖ Áp dụng với ví dụ gửi và rút tiền:

5/17/2023 Lập trình nâng cao 62


Phối hợp giữa các thread

5/17/2023 Lập trình nâng cao 63


Phối hợp giữa các thread

5/17/2023 Lập trình nâng cao 64


Phối hợp giữa các thread

Class Account (tiếp)

5/17/2023 Lập trình nâng cao 65


Phối hợp giữa các thread

5/17/2023 Lập trình nâng cao 66


Phối hợp giữa các thread

❖Khi một thread gọi await() theo một condition, thì:


▪ Thread đó sẽ đợi một tín hiệu signal để tiếp tục.
▪ Nếu không có thread nào gửi tín hiệu signal hoặc signalAll,
thread đó sẽ đợi mãi.
❖Một condition được tạo từ một đối tượng Lock.
▪ Để gọi phương thức của Condition, trước tiên phải sở hữu
khóa.
▪ Nếu gọi phương thức này mà không có khóa, thì sẽ ném ra
ngoại lệ IllegalMonitorStateException.

5/17/2023 Lập trình nâng cao 67


Thread
07 Communication

Built-in monitor
(wait, notify, notifyAll)

Lập trình nâng cao 68


Built-in monitor

❖Trước Java 5, sử dụng các built-in monitor: wait(),


notify(), notifyAll() để giao tiếp giữa các thread.
❖Lock & Condition hiệu quả và linh hoạt hơn so với
built-in monitor => Nên dùng Lock & Condition.
❖wait(), notify(), notifyAll() phải được gọi trong
synchronized method hoặc synchronized block. Nếu
không sẽ gặp ngoại lệ IllegalMonitorStateException.

5/17/2023 Lập trình nâng cao 69


Ví dụ sử dụng built-in monitor

5/17/2023 Lập trình nâng cao 71


Ví dụ sử dụng built-in monitor

Class MonitorDemo (tiếp)

5/17/2023 Lập trình nâng cao 72


Ví dụ sử dụng built-in monitor

5/17/2023 Lập trình nâng cao 73


Ví dụ sử dụng built-in monitor

5/17/2023 Lập trình nâng cao 74


Case study: Producer/Consumer
❖Giả sử có một chương trình như sau:
▪ Sử dụng một bộ đệm (buffer) để lưu các số nguyên và kích
thước bộ đệm bị giới hạn.
▪ Bộ đệm có phương thức write() để thêm số nguyên vào bộ
đệm và phương thức read() để lấy số nguyên ra khỏi bộ đệm
▪ Khi một tác vụ thêm một số nguyên vào bộ đệm, nếu bộ đệm
đầy, tác vụ sẽ đợi điều kiện notFull (tức là bộ đệm không
trống).
▪ Khi một tác vụ đọc số nguyên từ bộ đệm, nếu bộ đệm trống,
tác vụ sẽ đợi điều kiện notEmpty.

5/17/2023 Lập trình nâng cao 75


Case study: Producer/Consumer

❖Hàm main()

5/17/2023 Lập trình nâng cao 76


Case study: Producer/Consumer

❖Tác vụ ProducerTask để ghi số integer vào bộ đệm

5/17/2023 Lập trình nâng cao 77


Case study: Producer/Consumer

❖Tác vụ ConsumerTask để lấy số nguyên từ bộ đệm

5/17/2023 Lập trình nâng cao 78


Case study: Producer/Consumer

❖Lớp Buffer định nghĩa bộ đệm

5/17/2023 Lập trình nâng cao 79


Case study: Producer/Consumer

❖Lớp Buffer định nghĩa bộ đệm

5/17/2023 Lập trình nâng cao 80


Case study: Producer/Consumer

❖Kết quả chạy chương trình

5/17/2023 Lập trình nâng cao 81


Blocking Queues

❖Khi thêm vào phần tử mà blocking


queue đầy thì sẽ khiến thread bị
chặn lại
❖Khi xóa phần tử ra khỏi blocking
queue rỗng thì thread cũng sẽ bị
chặn lại
❖BlockingQueue là một interface kế
thừa từ interface Queue
▪ put: thêm một phần tử vào đuôi của
queue. Đợi nếu queue đang đầy
▪ take: Lấy một phần tử từ đầu của hàng
đợi. Đợi nếu queue đang rỗng

5/17/2023 Lập trình nâng cao 82


Blocking Queues

❖Java Collections Framework cung cấp các loại blocking


Queue:
▪ ArrayBlockingQueue
▪ LinkedBlockingQueue
▪ PriorityBlockingQueue

❖Phương thức put không bao giờ chặn unbounded


LinkedBlockingQueue và unbounded
PriorityBlockingQueue
5/17/2023 Lập trình nâng cao 83
Blocking Queues

❖Ví dụ Producer/Consumer sử dụng blocking queue

5/17/2023 Lập trình nâng cao 84


Blocking Queues

❖Ví dụ Producer/Consumer sử dụng blocking queue

5/17/2023 Lập trình nâng cao 85


Blocking Queues

❖Ví dụ Producer/Consumer sử dụng blocking queue

5/17/2023 Lập trình nâng cao 86


07 Semaphores

Lập trình nâng cao 87


Semaphore

❖Semaphore được sử dụng để giới hạn số lượng thread


truy nhập vào các tài nguyên chia sẻ (shared
resource)
❖Semaphore là một đối tượng điều khiển việc truy cập
vào một tài nguyên chia sẻ.
▪ Trước khi truy cập tài nguyên, một thread phải có “giấy phép”
từ semaphore.
▪ Sau khi kết thúc tài nguyên, thread phải trả lại “giấy phép” cho
semaphore.

Xin giấy phép từ semaphore.


Chờ nếu giấy phép không có sẵn

Trả lại giấy phép cho semaphore

5/17/2023 Lập trình nâng cao 88


Semaphore
❖Một số phương thức của Semaphore
▪ Semaphore(numberOfPermits: int): Tạo một semaphore với số
lượng giấy phép được chỉ định. Các chính sách công bằng được
đặt là false.
▪ Semaphore(numberOfPermits: int, fair: boolean): Tạo một
semaphore với số lượng giấy phép và được chỉ định chính sách
công bằng.
▪ acquire(): Xin giấy phép từ semaphore này. Nếu không có giấy
phép, luồng sẽ bị chặn cho đến khi có sẵn.
▪ release(): Phát hành giấy phép trở lại semaphore.

5/17/2023 Lập trình nâng cao 89


Ví dụ sử dụng Semaphore
❖Sử dụng Semaphore cho ví dụ Account
❖Mã nguồn AddPennyTask và main không thay đổi

5/17/2023 Lập trình nâng cao 90


Ví dụ sử dụng Semaphore
❖Sử dụng Semaphore để giới hạn số lượng thread được
phép truy nhập vào balance

5/17/2023 Lập trình nâng cao 91


Deadlock

❖Deadlock là trạng thái mà hai hay nhiều thread rơi


vào tình trạng chờ đợi lẫn nhau vì mỗi thread giữ một
tài nguyên và chờ đợi tài nguyên khác.
❖Ví dụ: Thread1 giữ tài nguyên là object1 và chờ đợi
tài nguyên object2 đang bị giữ bởi Thread2. Trong lúc
đó Thread2 lại chờ đợi Thread1 trả object1 để sử
dụng. Dẫn đến Thread1 và Thread2 chờ đợi lẫn nhau
mãi mãi
Assigned to object1 Waiting for

Thread Thread
1 2

Waiting for Assigned to


object2

5/17/2023 Lập trình nâng cao 92


Deadlock

❖Minh họa Thread1 và Thread2 bị deadlock

5/17/2023 Lập trình nâng cao 93


Ví dụ Deadlocks

5/17/2023 Lập trình nâng cao 94


Ví dụ Deadlocks

❖Hàm main

❖Kết quả chạy chương trình

5/17/2023 Lập trình nâng cao 95


Tránh Deadlock

❖Sử dụng kỹ thuật sắp xếp tài nguyên (resource


ordering).
▪ Chỉ định một thứ tự cho tất cả các đối tượng có khóa phải
được lấy và đảm bảo rằng mỗi luồng có được các khóa theo
thứ tự đó.
▪ Trong ví dụ trên, giả sử các tài nguyên được sắp xếp theo
object1 và object2. Khi sử dụng kỹ thuật sắp xếp tài nguyên
thì Thread2 phải có được một khóa trên object1 trước, sau đó
đến object2. Khi Thread1 có được một khóa trên object1,
Thread2 phải đợi một khóa trên object1. Do đó Thread1 có thể
có được một khóa trên object2 và không có deadlock nào xảy
ra.

5/17/2023 Lập trình nâng cao 96


Tránh Deadlock

❖Ví dụ minh họa

5/17/2023 Lập trình nâng cao 97


Synchronized
08
Collections

Lập trình nâng cao 98


Synchronized Collections

❖Các lớp trong Java Collection Frameworks không an


toàn theo luồng (thread-safe); nghĩa là, nội dung của
chúng có thể bị hỏng nếu chúng được nhiều thread
truy cập và cập nhật đồng thời.
❖Lớp Collections cung cấp sáu phương thức để gói một
tập hợp thành một thể hiện đồng bộ hóa. Các tập hợp
được tạo bằng các phương pháp này được gọi là trình
bao bọc đồng bộ hóa (synchronization wrappers)
❖Synchronized Collections được truy nhập và thay đổi
một cách an toàn bởi đồng thời nhiều thread.

5/17/2023 Lập trình nâng cao 99


Synchronized Collections

❖Gọi synchronizedCollection(c: Collection) sẽ trả về


một đối tượng Collection mới, trong đó tất cả các
phương thức truy cập và cập nhật collection c ban đầu
đều được đồng bộ hóa. Các phương thức này được
thực hiện bằng cách sử dụng từ khóa synchronized. Ví
dụ, phương thức add được triển khai như sau

5/17/2023 Lập trình nâng cao 100


Synchronized Collections
❖Các phương thức trong java.util.Vector, java.util.Stack,
java.util.Hashtable đã được đồng bộ hóa. Đây là các lớp
được giới thiệu trong JDK 1.0. Bắt đầu từ JDK 1.5, các
lớp trên đã được thay thế bằng java.util.ArrayList,
java.util.LinkedList, java.util.Map.
❖Khi cần đồng bộ hóa, sử dụng trình bao bọc đồng bộ
hóa.
❖Nếu sử dụng iterator để duyệt tập hợp trong khi có một
thread khác đang sửa đổi tập hợp thì sẽ gặp ngoại lệ
ConccurentModificationException. Để tránh lỗi này, ta
làm như sau:

5/17/2023 Lập trình nâng cao 101


Ví dụ List khi chưa đồng bộ hóa

5/17/2023 Lập trình nâng cao 102


Ví dụ List khi đã đồng bộ hóa

Chương trình khi chạy không xảy


ra Exception
5/17/2023 Lập trình nâng cao 103
Parallel
09
Programming

Lập trình nâng cao 104


Fork/Join Framework

❖JDK 7 giới thiệu Fork/Join Framework để lập trình


song song, sử dụng các bộ vi xử lý đa lõi.

5/17/2023 Lập trình nâng cao 105


Fork/Join Framework
❖Lớp ForkJoinTask định nghĩa tác vụ cho việc thực thi
không đồng bộ (asynchrnonous execution)

5/17/2023 Lập trình nâng cao 106


Fork/Join Framework

❖Fork/JoinsPool thực thi các tác vụ Fork/Join

5/17/2023 Lập trình nâng cao 107


Fork/Join Framework

5/17/2023 Lập trình nâng cao 108


Fork/Join Framework

5/17/2023 Lập trình nâng cao 109


Fork/Join Framework

❖Chương trình tìm số lớn nhất trong một list

5/17/2023 Lập trình nâng cao 110


Fork/Join Framework

5/17/2023 Lập trình nâng cao 111


Fork/Join Framework

5/17/2023 Lập trình nâng cao 112


Tổng kết

❖Khái niệm về lập trình đồng thời, lập trình song song
❖Runnable, Thread, sử dụng lớp Thread để thực thi các
tác vụ
❖Đồng bộ hóa giữa các thread
❖Giao tiếp giữa các thread

5/17/2023 Lập trình nâng cao 113


Tài liệu tham khảo

❖Introduction to Java Programming and Data


Structures, Y Daniel Liang.
❖Java How to Program, 10th, Early Objects, Paul Deitel
& Harvey Deitel.
❖Java for Everyone, Cay Horstmann.
❖Core Java - Volume I—Fundamentals, Tenth Edition,
Cay S. Horstmann.
❖Core Java - Volume II—Advanced Features, Tenth
Edition, Cay S. Horstmann.

Lập trình nâng cao 115

You might also like