public interface Runnable {
public void run();
}
Viết một class cài Runnable với hàm run() mô tả công việc cần chạy. Ví dụ:
public class Worker implements Runnable {
@Override
public void run() {
long sum = 0;
for (int i = 0; i < 10000000; i++) {
sum = sum + i; // do some time-consuming work
}
}
}
Để quan sát khi chạy thử lần đầu, bạn có thể thêm vài lệnh để viết gì đó ra màn hình.
public class Worker implements Runnable {
@Override
public void run() {
long sum = 0;
for (int i = 0; i < 10000000; i++) {
sum = sum + i; // do some work
// every n iterations, print an update
if (i % 1000000 == 0) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
}
Thread là một class trong thư viện Java đại diện cho một thread. Một chương trình Java khi chạy luôn có ít nhất 01 thread.
Lệnh Thread.currentThread().getName() trong đoạn code trên lấy tên của thread hiện đang chạy
(hàm Thread.currentThread() trả về thread hiện đang chạy, hàm thread.getName() trả về tên của thread).
Sửa đổi tại đoạn code trên sẽ cho bạn biết worker hiện đang chạy tại thread nào.
Hàm main() của chương trình chạy trên một thread được tạo sẵn tên là main. Bạn có thể chạy chương trình sau để xem Worker chạy trên thread main.
public static void main(String[] args) {
Worker worker = new Worker();
worker.run();
}
Để cho worker chạy trên một thread khác, không phải thread main mặc định.
Ta cần tạo một đối tượng thread mới cho đối tượng Worker và cho thread đó chạy
Để tạo thread cho một công việc loại Runnable, ta dùng constructor Thread(Runnable) của lớp Thread.
Thread a = new Thread(new Worker());Để cho thread a chạy, ta dùng lệnh
a.start();Chẳng hạn, dưới đây là một đoạn chương trình ví dụ tạo 02 thread để chạy song song 02 worker:
Thread a = new Thread(new Worker());
Thread b = new Thread(new Worker());
System.out.println(Thread.currentThread().getName() + " starting workers...");
a.start();
b.start();
// The current running thread (executing main()) blocks
// until both workers have finished
try {
a.join();
b.join();
}
catch (Exception ignored) {}
System.out.println(Thread.currentThread().getName() + " All done");
Đoạn try-catch với các lệnh join() là để đợi các thread a và b chạy xong rồi mới chạy tiếp, nếu không có đoạn
hàm main do ít việc sẽ chạy xong trước khi các thread a và b hoàn thành công việc của mình.
Bạn thử bỏ đoạn code trên và chạy lại để xem hiệu ứng.
Lớp Thread trong thư viện Java có API như sau:
public class Thread {
public Thread(Runnable R); // Thread ⇒ R.run()
public Thread(Runnable R, String name);
public void start(); // bắt đầu chạy thread (sẽ chạy hàm run của Runnable)
...
public String getName(); //lấy tên của thread
public void interrupt();
public boolean isAlive();
public void join(); //đợi đến khi thread chạy xong
public void setDaemon(boolean on);
public void setName(String name);
public void setPriority(int level);
public static Thread currentThread(); // trả về thread hiện hành
public static void sleep(long milliseconds); // cho thread tạm ngừng
public static void yield();
}
Bên cạnh cách tạo Runnable và dùng nó để khởi tạo thread, còn có một cách khác là viết lớp con của Thread và override lại hàm run của lớp Thread.
Tuy nhiên đó không phải cách được khuyên dùng do code của worker và code của thread sẽ bị rối vào nhau,
gây khó khăn khi cần chuyển sang các cách tiếp cận hiệu quả hơn chẳng hạn như Thread Pool (sẽ học sau).
Gợi ý: hàm changeColor() của các đối tượng Shape đổi màu của chúng. Các bạn có thể sửa nội dung nếu muốn đổi màu theo cách khác. Hãy tạo thêm các loại worker khác để làm các hiệu ứng như hình di chuyển hoặc hình phóng to, thu nhỏ.
Data data = new Data();
Thread a = new Thread(new DataRace(data));
Thread b = new Thread(new DataRace(data));
Thread c = new Thread(new DataRace(data));
a.start();
b.start();
c.start();
Trong đó, data là một đối tượng chứa một biến int có giá trị ban đầu bằng 0
public class Data {
int x = 0;
}
còn công việc của worker rất đơn giản là tăng x rồi lại giảm x:
public void run() {
for (int i = 0; i < 10000; i++) {
data.x++;
data.x--;
}
}
Nếu như không có tương tranh và chạy đua giữa các thread thì sau khi cả ba thread kết thúc,
giá trị của data.x phải không đổi. Tuy nhiên, thực tế là có chạy đua, do đó mỗi lần chạy
ta sẽ có một kết quả khác của data.x. Bạn hãy làm và chạy thử.
Nguyên nhân là do trình tự truy nhập.