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

TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA VÀ DEMO MINH HỌA

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 (249.67 KB, 25 trang )

HỌC VIỆN KỸ THUẬT MẬT MÃ
Khoa Công Nghệ Thông Tin

------

BÁO CÁO BÀI TẬP LỚN
ĐỀ TÀI: TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA VÀ DEMO MINH HỌA

Giáo viên hướng dẫn

: Lê Đức Thuận

Sinh viên thực hiện

:

Hà Nội, 09/2014

Hà Nội, 09/2014


MỤC LỤC
LỜI MỞ ĐẦU...........................................................................................................2
PHẦN A: ĐA LUỒNG TRONG JAVA..................................................................................2
I. Luồng trong Java là gì ?...................................................................................2
II. Cách tạo và quản lý luồng trong Java............................................................2
1. Tạo luồng bằng cách kế thừa từ lớp Thread....................................................2
2. Tạo luồng bằng cách thực hiện từ Interface Runnable....................................2
III. Một số thông tin liên quan đến luồng...........................................................2
1. ThreadID..........................................................................................................2
2. ThreadName.....................................................................................................2


3. Độ ưu tiên của luồng (Priority)........................................................................2
4. Độ lớn ngăn xếp của luồng (StackSize)...........................................................2
IV. Các trạng thái luồng.......................................................................................2
V. Các trạng thái luồng do java định nghĩa........................................................2
VI. Một số phương thức thơng dụng...................................................................2
VII. Nhóm luồng....................................................................................................2
VIII. Đồng bộ hóa.................................................................................................2
PHẦN B: DEMO................................................................................................................................2
I. Mơ tả chương trình....................................................................................................................2
II. Cấu trúc chương trình chương trình..............................................................................2
KẾT LUẬN...........................................................................................................................................2
TÀI LIỆU THAM KHẢO..............................................................................................................2


TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

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

LỜI MỞ ĐẦU
Ngày nay, dưới sự phát triển nhanh chóng của khoa học kỹ thuật, một kỷ
nguyên mới được mở ra, kỷ nguyên của công nghệ thông tin. Nhu cầu của loài
người ngày càng lớn, đặc biệt là các ngành khoa học kỹ thuật khác đều cần tới
sự hỗ trợ của công nghệ thông tin, mặc dù công nghệ phần cứng phát triển rất
nhanh, CPU với tốc độ xử lý ngày càng được nâng cao, nhưng lại nảy sinh nhiều
bài tốn trong thực tế địi hỏi thời gian thực hiện phải nhanh hơn nữa.
Vấn đề xử lý song song đang ngày càng được nghiên cứu nhiều để giải
quyết một số bài toán mà thực tế đang đặt ra, những vấn đề cần phải có kết quả
trong một khoảng thời gian nhất định. Vì vậy, việc nghiên cứu các giải thuật cho
xử lý song song đang là một yêu cầu và thách thức đối với các nhà lập trình
phần mềm.

Cùng với nhu cầu thiết yếu như vậy, vào năm 1991 Sun Microsystems đã
cho ra đời ngơn ngữ lập trình Java. Ngơn ngữ lập trình Java hỗ trợ cho việc xử
lý song song với cơ chế đa luồng.
Thông qua bài tập lớn mơn “Ngun lý Hệ điều hành” nhóm chúng em xin
trình bày một cách khái quát và cơ bản nhất về vấn đề “Phân luồng (Thread)
trong Java”.

3


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

TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

PHẦN A: ĐA LUỒNG TRONG JAVA
I. Luồng trong Java là gì?
Luồng là đơn vị nhỏ nhất trong java có thể thực hiện được 1 công việc riêng
biệt. Các luồng được quản lý bởi máy ảo java. Một ứng dụng java ngồi luồng
chính có thể có các luồng khác thực thi đồng thời. Đa luồng giúp cho các tác vụ
được xử lý độc lập giúp cơng việc được hồn thành nhanh chóng. Trình duyệt
web hay các chương trình chơi nhạc là 1 ví dụ điển hình về đa luồng.
- Khi duyệt 1 trang web, có rất nhiều hình ảnh, CSS, javascript... được tải
đồng thời bởi các luồng khác nhau.
- Khi play nhạc, chúng ta vẫn có thể tương tác được với nút điều khiển
như: Play, Pause, Next, Back ... vì luồng phát nhạc là luồng riêng biệt với
luồng tiếp nhận tương tác của người dùng.
Ưu điểm của đa luồng:
- Multithreading có chi phí thấp hơn Multiprocessing (về khơng gian, thời
gian). Trong multiprocessing các tiến trình chạy trên các địa chỉ riêng
biệt. Các luồng trong multithreading có thể chia sẻ cùng khơng gian bộ

nhớ.
- Các tiến trình trong multiprocessing gọi nhau phải chịu chi phí lớn hơn
các luồng trong multithreading.
- Multithreading cho phép chúng ta thiết kế chương trình hiệu quả hơn mà
sử dụng ít tài ngun CPU.
- Multithreading cịn cho phép chúng ta điều khiển cho các luồng sleep
một thời gian trong khi các luồng khác vẫn hoạt động mà không làm cho
hệ thống bị pause.

4


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

TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

II. Cách tạo và quản lý luồng trong Java
Trong java ta có thể tạo ra 1 luồng dễ dàng bằng cách tạo 1 đối tượng của
lớp được thừa kế từ lớp Thread (java.lang.Thread) hoặc implements từ giao diện
Runnable.

Khi nào implements từ interface Runnable?
- Khi bạn muốn kế thừa từ 1 lớp khác ngoài lớp Thread, nếu bạn kế thừa
nữa từ lớp Thread thì sẽ khơng được vì java khơng hỗ trợ đa thừa kế do
đó ta phải implements từ interface Runnable.
- Trong trường hợp cịn lại ta có thể kế thừa từ lớp Thread.
1. Tạo luồng bằng cách kế thừa từ lớp Thread
Để tạo luồng bằng cách tạo lớp kế thừa từ lớp Thread, ta phải làm các công
việc sau:
- Khai báo 1 lớp mới kế thừa từ lớp Thread.

- Override lại phương thức run ở lớp này, những gì trong phương thức run
sẽ được thực thi khi luồng bắt đầu chạy. Sau khi luồng chạy xong tất cả các
câu lệnh trong phương thức run thì luồng cũng tự hủy.
- Tạo 1 thể hiện (hay 1 đối tượng) của lớp ta vừa khai báo.
- Sau đó gọi phương thức start() của đối tượng này để bắt đầu thực thi
luồng.
Tuy ta khai báo những công việc cần làm của luồng trong phương thức run()
nhưng khi thực thi luồng ta phải gọi phương thức start(). Vì đây là phương thức
đặc biệt mà java xây dựng sẵn trong lớp Thread, phương thức này sẽ cấp phát tài
nguyên cho luồng mới rồi chạy phương thức run() ở luồng này. Vì vậy, nếu ta
gọi phương thức run() mà khơng gọi start() thì cũng như ta gọi 1 phương thức
của 1 đối tượng bình thường và phương thức vẫn chạy trên luồng mà gọi
phương thức chứ không chạy ở luồng mới tạo ra nên vẫn chỉ có 1 luồng chính
làm việc chứ ứng dụng vẫn không phải là đa luồng.
5


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

TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

Ví dụ:
- Khai báo lớp mới ThreadX kế thừa từ lớp Thread và override phương
thức run() :
public class ThreadX extends Thread{
private String prefix="";
public ThreadX(String p)
{
prefix=p;
}

public void run()
{
for(int i=0;i<100;i++)
System.out.println(prefix+i);
}
}

- Tạo các đối tượng thuộc lớp ThreadX và start() chúng :
public class Main {
public static void main(String[] args) {
ThreadX t1=new ThreadX("Luồng thứ 1:");
ThreadX t2=new ThreadX("Luồng thứ 2:");
ThreadX t3=new ThreadX("Luồng thứ 3:");
t1.start();
t2.start();
t3.start();
}}

Kết quả:

6


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

TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

Hình 1. Tạo luồng bằng cách kế thừa từ lớp Thread.

7



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

TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

2. Tạo luồng bằng cách thực hiện từ Interface Runnable
Để tạo luồng bằng cách hiện thực từ Interface Runnable, ta phải làm các
công việc sau:
- Khai báo 1 lớp mới implements từ Interface Runnable.
- Hiện thực phương thức run() ở lớp này, những gì trong phương thức run()
sẽ được thực thi khi luồng bắt đầu chạy. Sau khi luồng chạy xong tất cả các
câu lệnh trong phương thức run thì luồng cũng tự hủy.
- Tạo 1 thể hiện (hay 1 đối tượng) của lớp ta vừa khai báo.
(VD: Tên đối tượng là r1)
- Tạo 1 thể hiện của lớp Thread bằng phương thức khởi tạo:
Thread(Runnable target)

Với Runnable target là một đối tượng thuộc lớp được implements từ giao
diện Runnable.
Ví dụ:
- Khai báo lớp mới RunX implements từ interface Runnable và hiện thực
phương thức run():
public class RunX implements Runnable{
private String prefix="";
public RunX(String p)
{
prefix=p;
}
public void run()

{
for(int i=0;i<100;i++)
System.out.println(prefix+i);
}}

- Tạo các thể hiện của lớp RunX, Thread và start() chúng:
public class Main {
public static void main(String[] args) {
RunX r1=new RunX("Luồng thứ 1:");
RunX r2=new RunX("Luồng thứ 2:");
8


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

TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

RunX r3=new RunX("Luồng thứ 3:");
Thread t1=new Thread(r1);
Thread t2=new Thread(r2);
Thread t3=new Thread(r3);
t1.start();
t2.start();
t3.start();
}}

III. Một số thông tin liên quan đến luồng
Luồng được thành lập bởi 1 định danh (ThreadID), bộ đếm chương trình
(Counter), tập thanh ghi (Register) và ngăn xếp (Stack). Các luồng sẽ có 1 độ ưu
tiên (Priority) nhất định.

1. ThreadID
ThreadID là định danh của luồng, nó dùng để phân biệt với các luồng khác
cùng tiến trình hoặc cùng tập luồng. Đây là thông số mà máy ảo java tự tạo ra
khi ta tạo luồng nên ta không thể sửa đổi cũng như áp đặt thông số này khi tạo
luồng. Nhưng ta có thể lấy được nó thơng qua phương thức getId() của lớp
Thread.
Ví dụ:
public class Main {
public static void main(String[] args) {
ThreadX t1=new ThreadX("Luồng thứ 1:");
ThreadX t2=new ThreadX("Luồng thứ 2:");
ThreadX t3=new ThreadX("Luồng thứ 3:");
System.out.println("Tên luồng 1:"+t1.getName());
System.out.println("Tên luồng 2:"+t2.getName());
System.out.println("Tên luồng 3:"+t3.getName());
}
}

9


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

TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

10


TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA


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

Kết quả:

Hình 2. Ví dụ về ThreadID

2. ThreadName
ThreadName là tên của luồng, đây là thuộc tính mà ta có thể đặt hoặc không
đặt cho luồng. Nếu ta không đặt cho luồng thì máy ảo java sẽ tự đặt với quy tắc
sau: “Thread-” + Thứ tự luồng được tạo ra, bắt đầu từ 0.
Ví dụ:
public class Main {
public static void main(String[] args) {
ThreadX t1=new ThreadX("Luồng thứ 1:");
ThreadX t2=new ThreadX("Luồng thứ 2:");
ThreadX t3=new ThreadX("Luồng thứ 3:");
System.out.println("Tên luồng 1:"+t1.getName());
System.out.println("Tên luồng 2:"+t2.getName());
System.out.println("Tên luồng 3:"+t3.getName());
}
}

Kết quả:

Hình 3. Ví dụ về ThreadName

11


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


TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

Nếu muốn đặt tên cho luồng, ta có thể đặt tên thông qua phương thức khởi
tạo Thread(String name) hoặc phương thức void setName(String name).
Và ta có thể lấy tên luồng thông qua phương thức: Public String getName()
3. Độ ưu tiên của luồng (Priority)
Mỗi luồng có 1 độ ưu tiên nhất định. Đây sẽ là thông số quyết định mức ưu
tiên khi cấp phát CPU cho các luồng. Trong java, đế đặt độ ưu tiên cho 1 luồng
ta dùng phương thức void setPriority(int newPriority) với newPriority là giá
trị từ 1 đến 10.
Java có định nghĩa sẵn 3 mức ưu tiên chuẩn như sau:
- Thread.MAX_PRIORITY (giá trị 10)
- Thread.NORM_PRIORITY (giá trị 05)
- Thread.MIN_PRIORITY (giá trị 01)
Để lấy độ ưu tiên của 1 luồng, ta dùng phương thức int getPriority()
Ví dụ:
public class ThreadX extends Thread{
public ThreadX(String name)
{super(name);}
public void run()
{
for(int i=0;i<100;i++)
System.out.println("Tên luồng:"+getName()+", Độ ưu tiên:"+getPriority()+",i="+i);
}}

public static void main(String[] args) {
ThreadX t1=new ThreadX("Luồng thứ 1");
ThreadX t2=new ThreadX("Luồng thứ 2");
ThreadX t3=new ThreadX("Luồng thứ 3");


12


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

TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

t1.setPriority(1);
t2.setPriority(5);
t3.setPriority(10);
t1.start();
t2.start();
t3.start();
}}

Kết quả:

Hình 4. Ví dụ về Priority. Luồng 3 có độ ưu tiên lớn nên được cấp CPU cao.

4. Độ lớn ngăn xếp của luồng (StackSize)
StackSize là độ lớn của ngăn xếp (tính bằng byte) mà luồng có thể dùng
trong q trình thực thi. Nếu ta khơng quy định giá trị này hoặc đặt giá trị này
bằng 0 thì nó sẽ phụ thuộc vào giá trị mặc định mà JMV quy định và JMV sẽ tự
điều chỉnh cho hợp lý. Cịn nếu ta có quy định, thread sẽ chỉ cho phép dùng
Stack tới mức tối đa mà giá trị này quy định.

IV. Các trạng thái luồng
Sơ đồ dưới đây mô tả tổng quan về các trạng thái mà luồng có thề có trong
suốt vịng đời của mình và q trình chuyển tiếp giữa các trạng thái khác nhau.

13


TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

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

Hình 5. Các trạng thái luồng

New Thread: Đây là trạng thái khi luồng vừa được khởi tạo bằng phương
thức khởi tạo của lớp Thread nhưng chưa được start(). Ở trạng thái này, luồng
được tạo ra nhưng chưa được cấp phát tài nguyên và cũng chưa chạy. Nếu luồng
đang ở trạng thái này mà ta gọi các phương thức ép buộc stop, resume, suspend
… sẽ là nguyên nhân sảy ra ngoại lệ IllegalThreadStateException .
VD: Thread test=new Thread();
Runnable: Xét code sau:
Thread test=new Thread();
test.start()
Sau khi gọi phương thức start() thì luồng test đã được cấp phát tài nguyên và
các lịch điều phối CPU cho luồng test cũng bắt đầu có hiệu lực. Ở đây, chúng ta
dùng trạng thái là Runnable chứ không phải Running, vì như đã nói ở phần đầu
(Các mơ hình đa luồng) thì luồng khơng thực sự ln chạy mà tùy vào hệ thống
mà có sự điều phối CPU khác nhau.
Luồng sẽ ở trạng thái Runnable này từ khi luồng được gọi phương thức
start() và trước khi luồng kết thúc các công việc trong phương thức run() hoặc
trước khi sảy ra 1 ngoại lệ nào đó trong run() khiến run() bị kết thúc. Một dố
trường hợp nếu ta tự ý stop() hoặc destroy() luồng thì cũng khiến luồng rời khỏi
trạng thái runnable.
14



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

TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

Not Runnable: Một luồng bị rơi vào trạng thái “Not Runable” thì do 1
trong bốn nguyên nhân sau:
- Luồng bị tạm ngưng 1 khoảng nhất định do bị gọi phương thức sleep
(long milisecond)
- Luồng bị tạm ngưng cấp CPU do bị gọi phương thức suspend()
- Luồng bị gọi phương thức wait() để tạm ngưng để chờ 1 điều kiện nào đó
thay đổi khác với hiện tại.
- Luồng đang bị Block vì phải chờ I/O. Ví dụ: trong lúc chờ người dùng
nhập dữ liệu vào bàn phím, luồng bị Block
Ví dụ:
try {
Thread.sleep(10000);
} catch (InterruptedException e){}
Trong ví dụ trên, luồng sẽ tạm ngưng làm việc trong suốt 10000 mili giây
(10 giây). Tuy nhiên, cần lưu ý 1 điều là: khác với suspend(), khi sleep() luồng
vẫn được cấp CPU, nhưng luồng không làm gì cả, cịn suspend() luồng sẽ bị
ngưng cấp CPU.
Ngồi ra, cũng có 1 điều lưu ý khác : aLuồng bị “Not Runnable” bởi nguyên
nhân nào thì phải cho luồng chạy lại bởi 1 tác động thích hợp với đúng nguyên
nhân đó. Cụ thể như sau:
- Nếu luồng bị Sleep, phải đợi cho đến khi hết thời gian chỉ định sleep thì
luồng sẽ tự chạy lại
- Nếu luồng bị suspend() ta phải gọi phương thức resume() để tiếp tục lại
- Nếu luồng đang wait() , tức là chờ đợi 1 điều kiện nào đó thì luồng sở hữu
điều kiện đó phải gọi phương thức notify() hoặc notifyall() để thông báo cho

luồng chạy lại
- Nếu luồng đang bị Block vì I/O thì q trình I/O phải được hồn tất

15


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

TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

Như vậy, nếu luồng bị sleep 10 giây, mà nếu mới trải qua 5 giây, dù ta có
gọi phương thức resume() thì luồng vẫn khơng thể chạy lại mà sẽ xảy ra ngoại lệ
(Sẽ đề cập các ngoại lệ sâu hơn ở phần sau)
Dead: Một thread bị rơi vào trạng thái Dead do 1 trong 2 cách: Chết tự
nhiên hoặc bị ép dừng lại. Một thread sẽ chết 1 cách tự nhiên khi nó hồn thành
các lệnh trong phương thức run(). Bạn cũng có thể kết thúc 1 luồng bất kỳ lúc
nào bằng cách gọi phương thức stop().

V. Các trạng thái luồng do java định nghĩa
Ở phần trên, chúng ta đã đề cập chi tiết đến các trạng thái chung nhất của
luồng và các nguyên nhận đưa luồng vào trạng thái đó cũng như cách đưa luồng
thốt khỏi trạng thái đó. Ở phần này chúng ta sẽ đề cập đến cụ thể các trạng thái
của luồng theo cách định nghĩa của java ở những trường hợp cụ thể bởi các
nguyên nhận cụ thế.
Java định nghĩa các trạng thái của luồng trong Thread.State:
Thread.State.NEW
Thread ở trạng thái "New Thread" như đã đề cập ở phần trên
Thread.State.RUNNABLE
Thread ở trạng thái "Runnable" như đã đề cập ở phần trên
Thread.State.BLOCKED

Đây là 1 dạng của trạng thái “Not Runnable”. Thread chờ 1 đối tượng bị
lock bởi jmv Monitor (jmv Monitor sẽ để cập sâu ở chương "Đồng bộ hóa các
luồng").
Thread.State.WAITING
Đây là 1 dạng của trạng thái “Not Runnable”. Thread đang chờ 1 notify() từ
1 thread khác. Thread rơi vào trạng thái này do phương thức wait() hoặc join()

16


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

TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

Thread.State.TIMED_WAITING
Đây là 1 dạng của trạng thái “Not Runnable”. Thread đang chờ 1 notify() từ
1 thread khác trong 1 thời gian nhất định, Thread rơi vào trạng thái này do
phương thức wait(long timeout) hoặc join(long timeout)
Thread.State.TERMINATED
Thread đã hoàn thành công việc trong run() hoặc bị stop()

VI. Một số phương thức thơng dụng
activeCount(): Trả về số thread hiện tại có trong ThreadGroup.
checkAccess(): Kiểm tra xem thread đang chạy có quyền thay đổi nhóm
thread này hay khơng.
currentThread(): Đây là một phương thức tĩnh, có nghĩa là bạn có thể gọi
nó bất kỳ lúc nào ngay cả khi chưa khởi tạo luồng. Phương thức này trả về một
đối tượng của lớp Thread, đối tượng này chính là luồng đang được thực hiện
trong thời điểm hiện tại.
destroy(): Dừng hẳn luồng.

getId(): Trả về số nguyên nào đó là định danh của luồng.
getName(): Trả về tên của luồng
getPriority(): lấy độ ưu tiên của một Thread
getState(): Trả về trạng thái của luồng với các giá trị sau.
getThreadGroup(): Trả về nhóm Thread mà thread này thuộc về.
isAlive(): Trả về true nếu luồng vẫn đang hoạt động (luồng còn sống). Trả
về false nếu luồng đã thực hiện xong và dừng lại.
resume(): Tiếp tục chạy luồng.
setName(String name): Thay đổi tên của thread bằng tên name.
setPriority(int newPriority): Thay đổi tên của Thread.
17


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

TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

sleep(long millis): Tạm dừng luồng trong một khoảng thời gian millis giây.
start(): Dùng để bắt đầu chạy một luồng, nếu khơng gọi phương thức này
thì do dù bạn đã tạo một thể hiện của luồng thì nó vẫn ko có ý nghĩa gì cả.
stop(): Tạm dừng luồng lại khơng thực hiện nữa.
suspend(): Tạm dừng hoạt động của 1 luồng nào đó bằng các ngưng cung
cấp CPU cho luồng này.
yield(): Tạm dừng luồng hiện tại để cho luồng khác hoạt động.

VII. Nhóm luồng
Nhóm luồng là một tập hợp gồm nhiều luồng, khi ta tác động đến nhóm
luồng (chẳng hạn như đa tạm ngưng…) thì tất cả các luồng trong nhóm đều nhận
được cùng tác động đó, điều này là tiện lợi khi ta muốn quản lý nhiều luồng thực
hiện các tác vụ tương tự nhau.

Để tạo một nhóm luồng ta cần:
- Tạo ra một nhóm luồng bằng cách sử dụng phương thức tạo dựng của lớp
ThreadGroup():
ThreadGroup g = new ThreadGroup(“ThreadGroupName”);
- Đưa các luồng vào nhóm luồng dùng phương thức tạo dựng của lớp
Thread():
Thread() = new Thread(g, new ThreadClass(),”ThisThread”);

VIII. Đồng bộ hóa
Tất cả các luồng của một q trình thì được thực thi song song và độc lập
nhau nhưng lại cùng chia sẻ nhau một không gian địa chỉ của quá trình. Chính vì
vậy có thể dẫn đến khả năng đụng độ trong việc cập nhật dữ liệu dùng chung
của chương trình (biến, các tập tin).
VD: Một luồng có thể cố gắng đọc dữ liệu, trong khi luồng khác cố gắng
thay đổi dữ liệu ấy -> dữ liệu có thể sai.
18


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

TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

Trong những trường hợp này, bạn cần cho phép một luồng hồn thành trọn
vẹn tác vụ của nó, và rồi thì mới cho phép các luồng kế tiếp thực thi. Khi hai
hoặc nhiều hơn một luồng cần thâm nhập đến một tài nguyên được chia sẻ, bạn
cần chắc chắn rằng tài nguyên đó sẽ được sử dụng chỉ bởi một luồng tại một thời
điểm.
Đồng bộ hóa luồng (thread synchronization) giúp cho tại mỗi thời điểm chỉ
có một luồng có thể truy nhập vào đối tượng, còn các luồng khác phải đợi.
Các Thread được đồng bộ hóa trong Java sử dụng thông qua một bộ giám

sát (monitor). Hãy suy nghĩ rằng, một monitor là một đối tượng cho phép một
Thread truy cập vào một tài nguyên, chỉ có một Thread sử dụng một monitor tại
một thời điểm bất kỳ.
Một Thread chỉ có thể sở hữu một monitor nếu như khơng có Thread nào
đang sở hữu monitor đó. Khi một monitor đang ở trạng thái sẵn sàng thì một
Thread có thể sử monitor và nó có thể truy cập thẳng đến tài nguyên được tập
hợp với monitor đó. Ngược lại, Thread sẽ bị tạm treo cho đến khi monitor trở lại
trạng thái sẵn sàng.
Có hai cách để đồng bộ hóa các luồng: Sử dụng method được đồng bộ hóa
hoặc sử dụng phát biểu được đồng bộ hóa.
Sử dụng method được đồng bộ hóa:
Tất cả các đối tượng trong Java đề có một monitor. Một Thread có một
monitor bất kỳ khi nào một method được bổ sung từ khóa synchronized ở đầu
method đó được gọi.
Khi một luồng gọi phương thức synchronized, đối tượng sẽ bị khóa. Khi
luồng đó thực hiện xong phương thức, đối tượng sẽ được mở khóa.
Trong khi thực thi phương thức synchronized, một luồng có thể gọi wait()
để chuyển sang trạng thái chờ cho đến khi một điều kiện nào đó xảy ra. Khi
luồng đang chờ, đối tượng sẽ khơng bị khóa.
19


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

TÌM HIỂU CƠ CHẾ PHÂN LUỒNG TRONG JAVA

Khi thực hiện xong công việc trên đối tượng, một luồng cũng có thể thơng
báo (notify) cho các luồng khác đang chờ để truy nhập đối tượng.
Sử dụng phát biểu được đồng bộ hóa:
Việc đồng bộ hóa một phương thức là cách tốt nhất để hạn chế việc sử dụng

một phương thức tại một thời điểm. Tuy nhiên sẽ có những trường hợp mà bạn
khơng thể đồng bộ hóa một phương thức, chẳng hạn như khi bạn sử dụng một
classs được cung cấp bởi bên thứ ba. Trong những trường hợp như thế, bạn
không được phép truy nhập vào định nghĩa lớp, sẽ ngăn bạn sử dụng từ khóa
synchronized.
Một cách khác để sử dụng từ khóa synchronized là sử dụng phát biểu được
đồng bộ hóa. Một phát biểu được đồng bộ hóa chứa một block được đồng bộ
hóa, mà bên trong đó đặt những đối tượng và những phương thức được đồng bộ
hóa. Gọi các method chứa block được đồng bộ hóa xảy ra khi một Thread có
được monitor của đối tượng.

20



×