images
29/09/2020 05:13 am

Các cách lập trình đa luồng trong Java - Phần 1

Lập trình đa luồng là kỹ thuật lập trình rất phổ biến ngày nay khi mỗi máy tính đều hỗ trợ nhiều nhân và nhiều luồng hơn.

Lập trình đa luồng là kỹ thuật lập trình rất phổ biến ngày nay khi mỗi máy tính đều hỗ trợ nhiều nhân và nhiều luồng hơn. Tuy nhiên đây là một kỹ thuật lập trình không dễ vì nhiều yếu tố:


- Đảm bảo dữ liệu được toàn vẹn

- Đảm bảo chương trình hoạt động đúng trình tự mong muốn

- Đảm bảo hiệu năng chương trình được nâng cao (throughput), tốc độ (latency) chương trình không bị chậm lại (Context switching)


Ví dụ Threads: Các thread sẽ chạy song song không có thứ tự:


class Thread1 extends Thread {

   @Override

   public void run() {

       for (int i = 0; i < 200; i++) {

           System.out.println("This is thread 1");

       }

   }

}


class Thread2 extends Thread {

   @Override

   public void run() {

       for (int i = 0; i < 200; i++) {

           System.out.println("This is thread 2");

       }

   }

}

new Thread1().start();

new Thread2().start();



OUTPUT:


This is thread 1

This is thread 1

This is thread 2

This is thread 1

This is thread 1


Cách 1: Khởi tạo class là lớp con của Thread


class CalculationThread extends Thread {

   @Override

   public void run() {

       System.out.println("This is calculation thread");

   }

}


CalculationThread calThread = new CalculationThread();

calThread.start();


Cách 2: Khởi tạo class cài đặt interface Runnable


class CalculationRunnable implements Runnable {

   @Override

   public void run() {

       System.out.println("This is calculation thread from runnable class");

   }

}


Thread runnableThread = new Thread(new CalculationRunnable());

runnableThread.start();


Nhận xét


Về cơ bản, có 2 cách tạo Thread trong Java:

- Khởi tạo đối tượng Thread có phương thức run()

- Khởi tạo đối tượng Thread và truyền vào đối tượng được cài đặt interface Runnable


Thread sử dụng Lambda


Ngắn gọn hơn, bạn có thể dùng Lambda để tạo thread trong Java như sau:


Thread t2 = new Thread(()-> {

   System.out.println("This is new thread 2");

});


t2.start();



Cách 3: Sử dụng ThreadPool

ThreadPool cho phép bạn sử dụng đa luồng trong Java bằng cách khởi tạo một pool các Thread, sau đó bạn sẽ “gửi” vào đó các tác vụ, ThreadPool sẽ tự sinh ra thread để thực hiện các tác vụ mà bạn yêu cầu.


Có thể hiểu là bạn không cần phải tường minh tạo Thread. ThreadPool sẽ tạo và quản lý Thread. Bạn sẽ nhẹ đầu hơn thôi.


ThreadPool cho phép bạn gửi vào task và nhận về một Future.

Future có thể trả về kết quả hoặc không. Thông qua Future bạn có thể biết task đã chạy xong hay chưa.


Ví dụ ThreadPool


// Khởi tạo threadpool với số lượng thread là 2

ExecutorService executorService = Executors.newFixedThreadPool(2)


//Gửi vào ThreadPool một runnable task: executorService.submit(runnable task)

executorService.submit(() -> System.out.println("this task run in threadpool"));


// Gửi vào thread pool một task tính toán và lấy kết quả là một Future. Ví dụ ta 

// tính toán tổng từ 1 tới 1000000:

Future<Integer> sum = executorService.submit(() -> {

   int localSum = 0;

   for(int i = 0; i < 1000000; i++) {

       localSum += i;

   }

   return localSum;

});


Thay đổi N từ 1 tỷ -> 2 tỷ nhé


public class ExecutorServiceMain {

   static long N = 2000000000;

   public static void main(String[] args) throws InterruptedException {

       sum1();

       sum2();

   }


   static void sum2() {

       long start = System.currentTimeMillis();

       long localSum1 = 0;

       for(int i = 0; i < N; i++) {

           localSum1 += i;

       }

       long localSum2 = 0;

       for(int i = 0; i < N; i++) {

           localSum2 += i;

       }

       System.out.println("Sum 2: Total execution time ms: " + (System.currentTimeMillis() - start));

   }



   static void sum1() throws InterruptedException {

       ExecutorService executorService = Executors.newFixedThreadPool(2);

       long start = System.currentTimeMillis();

       Future<Long> sum = executorService.submit(() -> {

           long localSum = 0;

           for(int i = 0; i < N; i++) {

               localSum += i;

           }

           return localSum;

       });

       Future<Long> sum2 = executorService.submit(() -> {

           long localSum = 0;

           for(int i = 0; i < N; i++) {

               localSum += i;

           }

           return localSum;

       });

       while (!sum.isDone() || !sum2.isDone()) {

           Thread.sleep(5);

       }

       System.out.println("Sum 1: Total execution time ms: " + (System.currentTimeMillis() - start));

       executorService.shutdown();

   }

}


Mời các bạn đọc thêm bài Java Queue - Tại sao phải dùng kiểu cấu trúc dữ liệu này?


và bài Các cách lập trình đa luồng trong Java - Phần 2.

- Tech Zone -


Thư giãn chút nào!!!

Bài viết liên quan