CodeGym/Blog Java/Ngẫu nhiên/Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java...
John Squirrels
Mức độ
San Francisco

Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core. Phần 2

Xuất bản trong nhóm
Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core. Phần 1Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core.  Phần 2 - 1

đa luồng

24. Làm cách nào để tạo một luồng mới trong Java?

Bằng cách này hay cách khác, một luồng được tạo bằng lớp Thread. Nhưng có nhiều cách khác nhau để làm điều này…
  1. Kế thừa java.lang.Thread .
  2. Triển khai giao diện java.lang.Runnable — hàm tạo của lớp Thread nhận một đối tượng Runnable.
Hãy nói về từng người trong số họ.

Kế thừa lớp Thread

Trong trường hợp này, chúng tôi tạo lớp kế thừa java.lang.Thread . Nó có một phương thức run() , và đó chính là thứ chúng ta cần. Tất cả cuộc sống và logic của luồng mới sẽ nằm trong phương thức này. Nó giống như một phương thức chính cho luồng mới. Sau đó, tất cả những gì còn lại là tạo một đối tượng của lớp chúng ta và gọi phương thức start() . Thao tác này sẽ tạo một luồng mới và bắt đầu thực thi logic của nó. Hãy cùng xem:
/**
* An example of how to create threads by inheriting the {@link Thread} class.
*/
class ThreadInheritance extends Thread {

   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName());
   }

   public static void main(String[] args) {
       ThreadInheritance threadInheritance1 = new ThreadInheritance();
       ThreadInheritance threadInheritance2 = new ThreadInheritance();
       ThreadInheritance threadInheritance3 = new ThreadInheritance();
       threadInheritance1.start();
       threadInheritance2.start();
       threadInheritance3.start();
   }
}
Đầu ra của bàn điều khiển sẽ giống như thế này:
Chủ đề-1 Chủ đề-0 Chủ đề-2
Đó là, ngay cả ở đây, chúng tôi thấy rằng các luồng không được thực thi theo thứ tự, mà đúng hơn là JVM thấy phù hợp để chạy chúng :)

Triển khai giao diện Runnable

Nếu bạn phản đối kế thừa và/hoặc đã kế thừa một số lớp khác, bạn có thể sử dụng giao diện java.lang.Runnable . Ở đây, chúng tôi làm cho lớp của chúng tôi triển khai giao diện này bằng cách triển khai phương thức run() , giống như trong ví dụ trên. Tất cả những gì còn lại là tạo các đối tượng Thread . Có vẻ như càng nhiều dòng mã thì càng tệ. Nhưng chúng ta biết thừa kế nguy hiểm như thế nào và tốt hơn hết là nên tránh nó bằng mọi cách;) Hãy xem:
/**
* An example of how to create threads from the {@link Runnable} interface.
* It's easier than easy — we implement this interface and then pass an instance of our object
* to the constructor.
*/
class ThreadInheritance implements Runnable {

   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName());
   }

   public static void main(String[] args) {
       ThreadInheritance runnable1 = new ThreadInheritance();
       ThreadInheritance runnable2 = new ThreadInheritance();
       ThreadInheritance runnable3 = new ThreadInheritance();

       Thread threadRunnable1 = new Thread(runnable1);
       Thread threadRunnable2 = new Thread(runnable2);
       Thread threadRunnable3 = new Thread(runnable3);

       threadRunnable1.start();
       threadRunnable2.start();
       threadRunnable3.start();
   }
}
Và đây là kết quả:
Chủ đề-0 Chủ đề-1 Chủ đề-2

25. Đâu là sự khác biệt giữa process và thread?

Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core.  Phần 2 - 2Một quy trình và một luồng khác nhau theo các cách sau:
  1. Một chương trình đang chạy được gọi là một tiến trình, nhưng một luồng là một phần của một tiến trình.
  2. Các quy trình là độc lập, nhưng các luồng là các phần của một quy trình.
  3. Các tiến trình có các không gian địa chỉ khác nhau trong bộ nhớ, nhưng các luồng chia sẻ một không gian địa chỉ chung.
  4. Chuyển đổi ngữ cảnh giữa các luồng nhanh hơn chuyển đổi giữa các quy trình.
  5. Giao tiếp giữa các quá trình chậm hơn và đắt hơn so với giao tiếp giữa các luồng.
  6. Mọi thay đổi trong tiến trình cha không ảnh hưởng đến tiến trình con, nhưng những thay đổi trong luồng cha có thể ảnh hưởng đến luồng con.

26. Lợi ích của đa luồng là gì?

  1. Đa luồng cho phép một ứng dụng/chương trình luôn đáp ứng với đầu vào, ngay cả khi nó đang chạy một số tác vụ nền;
  2. Đa luồng giúp hoàn thành tác vụ nhanh hơn vì các luồng chạy độc lập;
  3. Đa luồng cung cấp khả năng sử dụng bộ nhớ cache tốt hơn, vì các luồng có thể truy cập tài nguyên bộ nhớ dùng chung;
  4. Đa luồng làm giảm số lượng máy chủ cần thiết, bởi vì một máy chủ có thể chạy nhiều luồng đồng thời.

27. Các trạng thái trong vòng đời của luồng là gì?

Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core.  Phần 2 - 3
  1. Mới: Ở trạng thái này, đối tượng Chủ đề được tạo bằng toán tử mới, nhưng một chủ đề mới chưa tồn tại. Chuỗi không bắt đầu cho đến khi chúng ta gọi phương thức start() .
  2. Runnable: Ở trạng thái này, luồng đã sẵn sàng chạy sau start() phương thức được gọi. Tuy nhiên, nó vẫn chưa được chọn bởi bộ lập lịch luồng.
  3. Đang chạy: Ở trạng thái này, bộ lập lịch luồng chọn một luồng từ trạng thái sẵn sàng và chạy.
  4. Đang chờ/Bị chặn: ở trạng thái này, một luồng không chạy, nhưng nó vẫn tồn tại hoặc đang chờ một luồng khác hoàn thành.
  5. Chết/Chấm dứt: khi một luồng thoát khỏi phương thức run() , nó ở trạng thái chết hoặc kết thúc.

28. Có thể chạy một luồng hai lần không?

Không, chúng tôi không thể khởi động lại một luồng, bởi vì sau khi một luồng bắt đầu và chạy, nó sẽ chuyển sang trạng thái Chết. Nếu chúng ta cố bắt đầu một luồng hai lần, một ngoại lệ java.lang.IllegalThreadStateException sẽ bị ném ra. Hãy cùng xem:
class DoubleStartThreadExample extends Thread {

   /**
    * Simulate the work of a thread
    */
   public void run() {
	// Something happens. At this state, this is not essential.
   }

   /**
    * Start the thread twice
    */
   public static void main(String[] args) {
       DoubleStartThreadExample doubleStartThreadExample = new DoubleStartThreadExample();
       doubleStartThreadExample.start();
       doubleStartThreadExample.start();
   }
}
Sẽ có một ngoại lệ ngay khi quá trình thực thi bắt đầu lần thứ hai của cùng một luồng. Hãy tự mình thử;) Thà xem cái này một lần còn hơn nghe về nó hàng trăm lần.

29. Nếu bạn gọi trực tiếp run() mà không gọi start() thì sao?

Có, bạn chắc chắn có thể gọi phương thức run() , nhưng một luồng mới sẽ không được tạo và phương thức này sẽ không chạy trên một luồng riêng biệt. Trong trường hợp này, chúng ta có một đối tượng bình thường gọi một phương thức thông thường. Nếu chúng ta đang nói về phương thức start() thì đó là một vấn đề khác. Khi phương thức này được gọi, JVM bắt đầu một luồng mới. Đến lượt chủ đề này gọi phương thức của chúng tôi;) Bạn không tin à? Đây, hãy thử xem:
class ThreadCallRunExample extends Thread {

   public void run() {
       for (int i = 0; i < 5; i++) {
           System.out.print(i);
       }
   }

   public static void main(String args[]) {
       ThreadCallRunExample runExample1 = new ThreadCallRunExample();
       ThreadCallRunExample runExample2 = new ThreadCallRunExample();

       // Two ordinary methods will be called in the main thread, one after the other.
       runExample1.run();
       runExample2.run();
   }
}
Và đầu ra của giao diện điều khiển sẽ trông như thế này:
0123401234
Như bạn có thể thấy, không có chuỗi nào được tạo. Mọi thứ hoạt động giống như trong một lớp học bình thường. Đầu tiên, phương thức của đối tượng đầu tiên được thực thi, sau đó là đối tượng thứ hai.

30. Chủ đề daemon là gì?

Một luồng daemon là một luồng thực hiện các tác vụ ở mức ưu tiên thấp hơn so với một luồng khác. Nói cách khác, công việc của nó là thực hiện các tác vụ phụ chỉ cần được thực hiện cùng với một luồng (chính) khác. Có nhiều luồng daemon chạy tự động, chẳng hạn như bộ sưu tập rác, bộ hoàn thiện, v.v.

Tại sao Java chấm dứt một luồng daemon?

Mục đích duy nhất của luồng daemon là cung cấp hỗ trợ nền cho luồng của người dùng. Theo đó, nếu luồng chính bị chấm dứt, thì JVM sẽ tự động chấm dứt tất cả các luồng daemon của nó.

Các phương thức của lớp Thread

Lớp java.lang.Thread cung cấp hai phương thức để làm việc với luồng daemon:
  1. public void setDaemon(boolean status) — Phương thức này cho biết liệu đây có phải là một luồng daemon hay không. Mặc định là sai . Điều này có nghĩa là sẽ không có chủ đề daemon nào được tạo trừ khi bạn nói cụ thể như vậy.
  2. public boolean isDaemon() — Phương thức này về cơ bản là một getter cho biến daemon mà chúng ta đã đặt bằng phương thức trước đó.
Ví dụ:
class DaemonThreadExample extends Thread {

   public void run() {
       // Checks whether this thread is a daemon
       if (Thread.currentThread().isDaemon()) {
           System.out.println("daemon thread");
       } else {
           System.out.println("user thread");
       }
   }

   public static void main(String[] args) {
       DaemonThreadExample thread1 = new DaemonThreadExample();
       DaemonThreadExample thread2 = new DaemonThreadExample();
       DaemonThreadExample thread3 = new DaemonThreadExample();

       // Make thread1 a daemon thread.
       thread1.setDaemon(true);

       System.out.println("daemon? " + thread1.isDaemon());
       System.out.println("daemon? " + thread2.isDaemon());
       System.out.println("daemon? " + thread3.isDaemon());

       thread1.start();
       thread2.start();
       thread3.start();
   }
}
Đầu ra bảng điều khiển:
quỷ? yêu tinh thực sự? daemon giả? chủ đề daemon sai chủ đề người dùng chủ đề người dùng
Từ đầu ra, chúng ta thấy rằng bên trong chính luồng đó, chúng ta có thể sử dụng phương thức tĩnh currentThread() để tìm ra đó là luồng nào. Ngoài ra, nếu chúng ta có một tham chiếu đến đối tượng luồng, chúng ta cũng có thể tìm ra trực tiếp từ nó. Điều này cung cấp mức độ cấu hình cần thiết.

31. Có thể biến thread thành daemon sau khi nó được tạo không?

Không. Nếu bạn cố làm điều này, bạn sẽ nhận được IllegalThreadStateException . Điều này có nghĩa là chúng ta chỉ có thể tạo một luồng daemon trước khi nó bắt đầu. Ví dụ:
class SetDaemonAfterStartExample extends Thread {

   public void run() {
       System.out.println("Working...");
   }

   public static void main(String[] args) {
       SetDaemonAfterStartExample afterStartExample = new SetDaemonAfterStartExample();
       afterStartExample.start();

       // An exception will be thrown here
       afterStartExample.setDaemon(true);
   }
}
Đầu ra bảng điều khiển:
Đang hoạt động... Ngoại lệ trong luồng "chính" java.lang.IllegalThreadStateException tại java.lang.Thread.setDaemon(Thread.java:1359) tại SetDaemonAfterStartExample.main(SetDaemonAfterStartExample.java:14)

32. Móc tắt máy là gì?

Móc tắt máy là một luồng được gọi ngầm trước khi máy ảo Java (JVM) bị tắt. Do đó, chúng ta có thể sử dụng nó để giải phóng tài nguyên hoặc lưu trạng thái khi máy ảo Java tắt bình thường hoặc bất thường. Chúng ta có thể thêm một móc tắt máy bằng phương pháp sau:
Runtime.getRuntime().addShutdownHook(new ShutdownHookThreadExample());
Như thể hiện trong ví dụ:
/**
* A program that shows how to start a shutdown hook thread,
* which will be executed right before the JVM shuts down
*/
class ShutdownHookThreadExample extends Thread {

   public void run() {
       System.out.println("shutdown hook executed");
   }

   public static void main(String[] args) {

       Runtime.getRuntime().addShutdownHook(new ShutdownHookThreadExample());

       System.out.println("Now the program is going to fall asleep. Press Ctrl+C to terminate it.");
       try {
           Thread.sleep(60000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }
}
Đầu ra bảng điều khiển:
Bây giờ chương trình sẽ đi vào giấc ngủ. Nhấn Ctrl + C để chấm dứt nó. móc tắt máy thực hiện

33. Đồng bộ hóa là gì?

Trong Java, đồng bộ hóa là khả năng kiểm soát quyền truy cập của nhiều luồng vào bất kỳ tài nguyên được chia sẻ nào. Khi nhiều luồng cố gắng thực hiện cùng một tác vụ đồng thời, bạn có thể nhận được kết quả không chính xác. Để khắc phục sự cố này, Java sử dụng đồng bộ hóa, cho phép mỗi lần chỉ chạy một luồng. Đồng bộ hóa có thể đạt được theo ba cách:
  • Đồng bộ hóa một phương pháp
  • Đồng bộ hóa một khối cụ thể
  • đồng bộ hóa tĩnh

Đồng bộ hóa một phương pháp

Một phương thức đồng bộ hóa được sử dụng để khóa một đối tượng cho bất kỳ tài nguyên được chia sẻ nào. Khi một luồng gọi một phương thức được đồng bộ hóa, nó sẽ tự động lấy khóa của đối tượng và giải phóng nó khi luồng hoàn thành nhiệm vụ của nó. Để thực hiện công việc này, bạn cần thêm từ khóa được đồng bộ hóa . Chúng ta có thể thấy điều này hoạt động như thế nào bằng cách xem một ví dụ:
/**
* An example where we synchronize a method. That is, we add the synchronized keyword to it.
* There are two authors who want to use one printer. Each of them has composed their own poems
* And of course they don’t want their poems mixed up. Instead, they want work to be performed in * * * order for each of them
*/
class Printer {

   synchronized void print(List<String> wordsToPrint) {
       wordsToPrint.forEach(System.out::print);
       System.out.println();
   }

   public static void main(String args[]) {
       // One object for two threads
       Printer printer  = new Printer();

       // Create two threads
       Writer1 writer1 = new Writer1(printer);
       Writer2 writer2 = new Writer2(printer);

       // Start them
       writer1.start();
       writer2.start();
   }
}

/**
* Author No. 1, who writes an original poem.
*/
class Writer1 extends Thread {
   Printer printer;

   Writer1(Printer printer) {
       this.printer = printer;
   }

   public void run() {
       List<string> poem = Arrays.asList("I ", this.getName(), " Write", " A Letter");
       printer.print(poem);
   }

}

/**
* Author No. 2, who writes an original poem.
*/
class Writer2 extends Thread {
   Printer printer;

   Writer2(Printer printer) {
       this.printer = printer;
   }

   public void run() {
       List<String> poem = Arrays.asList("I Do Not ", this.getName(), " Not Write", " No Letter");
       printer.print(poem);
   }
}
Và đầu ra giao diện điều khiển là thế này:
Tôi Chủ đề-0 Viết Thư Tôi Không Chủ đề-1 Không Viết Không Thư

khối đồng bộ hóa

Một khối được đồng bộ hóa có thể được sử dụng để thực hiện đồng bộ hóa trên bất kỳ tài nguyên cụ thể nào trong một phương thức. Giả sử rằng trong một phương pháp lớn (vâng, bạn không nên viết chúng, nhưng đôi khi chúng xảy ra), vì một số lý do, bạn chỉ cần đồng bộ hóa một phần nhỏ. Nếu bạn đặt tất cả mã của phương thức vào một khối được đồng bộ hóa, nó sẽ hoạt động giống như phương thức được đồng bộ hóa. Cú pháp trông như thế này:
synchronized ("object to be locked") {
   // The code that must be protected
}
Để tránh lặp lại ví dụ trước, chúng tôi sẽ tạo các luồng bằng cách sử dụng các lớp ẩn danh, tức là chúng tôi sẽ triển khai ngay giao diện Runnable.
/**
* This is how a synchronization block is added.
* Inside the block, you need to specify which object's mutex will be acquired.
*/
class Printer {

   void print(List<String> wordsToPrint) {
       synchronized (this) {
           wordsToPrint.forEach(System.out::print);
       }
       System.out.println();
   }

   public static void main(String args[]) {
       // One object for two threads
       Printer printer = new Printer();

       // Create two threads
       Thread writer1 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I ", "Writer1", " Write", " A Letter");
               printer.print(poem);
           }
       });
       Thread writer2 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I Do Not ", "Writer2", " Not Write", " No Letter");
               printer.print(poem);
           }
       });

       // Start them
       writer1.start();
       writer2.start();
   }
}

}
Và đầu ra giao diện điều khiển là thế này:
Tôi Nhà Văn1 Viết Thư Tôi Không Nhà Văn2 Không Viết Không Thư

đồng bộ hóa tĩnh

Nếu bạn tạo một phương thức tĩnh được đồng bộ hóa, thì việc khóa sẽ xảy ra trên lớp chứ không phải đối tượng. Trong ví dụ này, chúng tôi thực hiện đồng bộ hóa tĩnh bằng cách áp dụng từ khóa được đồng bộ hóa cho một phương thức tĩnh:
/**
* This is how a synchronization block is added.
* Inside the block, you need to specify which object's mutex will be acquired.
*/
class Printer {

   static synchronized void print(List<String> wordsToPrint) {
       wordsToPrint.forEach(System.out::print);
       System.out.println();
   }

   public static void main(String args[]) {

       // Create two threads
       Thread writer1 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I ", "Writer1", " Write", " A Letter");
               Printer.print(poem);
           }
       });
       Thread writer2 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("I Do Not ", "Writer2", " Not Write", " No Letter");
               Printer.print(poem);
           }
       });

       // Start them
       writer1.start();
       writer2.start();
   }
}
Và đầu ra giao diện điều khiển là thế này:
Tôi Không Nhà Văn2 Không Viết Không Thư Tôi Nhà Văn1 Viết Thư

34. Biến lưu lượng là gì?

Trong lập trình đa luồng, từ khóa dễ bay hơi được sử dụng để đảm bảo an toàn cho luồng. Khi một biến có thể thay đổi được sửa đổi, thay đổi đó sẽ hiển thị đối với tất cả các luồng khác, do đó, một biến có thể được sử dụng bởi một luồng tại một thời điểm. Bằng cách sử dụng từ khóa dễ bay hơi , bạn có thể đảm bảo rằng một biến là an toàn cho luồng và được lưu trữ trong bộ nhớ dùng chung và các luồng đó sẽ không lưu trữ biến đó trong bộ đệm của chúng. điều này như thế nào?
private volatile AtomicInteger count;
Chúng tôi chỉ thêm dễ bay hơi vào biến. Nhưng hãy nhớ rằng điều này không có nghĩa là hoàn toàn an toàn cho luồng... Xét cho cùng, các thao tác trên biến có thể không phải là nguyên tử. Điều đó nói rằng, bạn có thể sử dụng các lớp Nguyên tử thực hiện các hoạt động nguyên tử, tức là trong một lệnh CPU. Có nhiều lớp như vậy trong gói java.util.concurrent.atomic .

35. Bế tắc là gì?

Trong Java, bế tắc là điều có thể xảy ra như một phần của đa luồng. Bế tắc có thể xảy ra khi một luồng đang đợi khóa của một đối tượng được một luồng khác lấy được và luồng thứ hai đang đợi khóa của đối tượng được luồng đầu tiên lấy được. Điều này có nghĩa là hai luồng đang đợi nhau và việc thực thi mã của chúng không thể tiếp tục. Top 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core.  Phần 2 - 4Hãy xem xét một ví dụ có một lớp triển khai Runnable. Hàm tạo của nó chiếm hai tài nguyên. Phương thức run() lấy khóa cho chúng theo thứ tự. Nếu bạn tạo hai đối tượng của lớp này và chuyển các tài nguyên theo một thứ tự khác, thì bạn có thể dễ dàng gặp bế tắc:
class DeadLock {

   public static void main(String[] args) {
       final Integer r1 = 10;
       final Integer r2 = 15;

       DeadlockThread threadR1R2 = new DeadlockThread(r1, r2);
       DeadlockThread threadR2R1 = new DeadlockThread(r2, r1);

       new Thread(threadR1R2).start();
       new Thread(threadR2R1).start();
   }
}

/**
* A class that accepts two resources.
*/
class DeadlockThread implements Runnable {

   private final Integer r1;
   private final Integer r2;

   public DeadlockThread(Integer r1, Integer r2) {
       this.r1 = r1;
       this.r2 = r2;
   }

   @Override
   public void run() {
       synchronized (r1) {
           System.out.println(Thread.currentThread().getName() + " acquired resource: " + r1);

           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }

           synchronized (r2) {
               System.out.println(Thread.currentThread().getName() + " acquired resource: " + r2);
           }
       }
   }
}
Đầu ra bảng điều khiển:
Luồng thứ nhất lấy được tài nguyên thứ nhất Luồng thứ hai lấy được tài nguyên thứ hai

36. Làm thế nào để bạn tránh bế tắc?

Bởi vì chúng tôi biết bế tắc xảy ra như thế nào, chúng tôi có thể rút ra một số kết luận...
  • Trong ví dụ trên, bế tắc xảy ra do chúng ta đã khóa lồng nhau. Đó là, chúng ta có một khối được đồng bộ hóa bên trong một khối được đồng bộ hóa. Để tránh điều này, thay vì lồng nhau, bạn cần tạo một lớp trừu tượng mới cao hơn, di chuyển đồng bộ hóa lên cấp cao hơn và loại bỏ khóa lồng nhau.
  • Bạn càng khóa nhiều thì càng có nhiều khả năng xảy ra bế tắc. Do đó, mỗi khi bạn thêm một khối được đồng bộ hóa, bạn cần suy nghĩ xem liệu bạn có thực sự cần nó hay không và liệu bạn có thể tránh thêm một khối mới hay không.
  • Sử dụng Thread.join() . Bạn cũng có thể rơi vào bế tắc trong khi một luồng chờ một luồng khác. Để tránh sự cố này, bạn có thể cân nhắc đặt thời gian chờ cho phương thức join() .
  • Nếu chúng ta có một luồng, thì sẽ không có bế tắc;)

37. Điều kiện cuộc đua là gì?

Nếu các cuộc đua ngoài đời thực liên quan đến ô tô, thì các cuộc đua trong đa luồng liên quan đến các luồng. Nhưng tại sao? :/ Có hai luồng đang chạy và có thể truy cập cùng một đối tượng. Và họ có thể cố gắng cập nhật trạng thái của đối tượng được chia sẻ cùng một lúc. Mọi thứ rõ ràng cho đến nay, phải không? Các luồng được thực thi song song theo nghĩa đen (nếu bộ xử lý có nhiều hơn một lõi) hoặc tuần tự, với bộ xử lý phân bổ các lát thời gian xen kẽ. Chúng tôi không thể quản lý các quá trình này. Điều này có nghĩa là khi một luồng đọc dữ liệu từ một đối tượng, chúng tôi không thể đảm bảo rằng nó sẽ có thời gian để thay đổi đối tượng TRƯỚC KHI một số luồng khác làm như vậy. Những vấn đề như vậy phát sinh khi chúng ta có các tổ hợp "kiểm tra và hành động" này. Điều đó nghĩa là gì? Giả sử chúng ta có một câu lệnh if mà phần thân của nó thay đổi chính điều kiện if, ví dụ:
int z = 0;

// Check
if (z < 5) {
// Act
   z = z + 5;
}
Hai luồng có thể đồng thời nhập khối mã này khi z vẫn bằng 0 và sau đó cả hai luồng có thể thay đổi giá trị của nó. Kết quả là, chúng tôi sẽ không nhận được giá trị như mong đợi là 5. Thay vào đó, chúng tôi sẽ nhận được 10. Bạn tránh điều này như thế nào? Bạn cần lấy khóa trước khi kiểm tra và hành động, sau đó nhả khóa sau đó. Tức là, bạn cần để luồng đầu tiên nhập khối if , thực hiện tất cả các hành động, thay đổi z và chỉ sau đó mới cho luồng tiếp theo cơ hội thực hiện điều tương tự. Nhưng chuỗi tiếp theo sẽ không nhập khối if , vì z bây giờ sẽ là 5:
// Acquire the lock for z
if (z < 5) {
   z = z + 5;
}
// Release z's lock
===================================================

Thay cho lời kết

Tôi muốn nói lời cảm ơn đến tất cả những người đã đọc đến cuối. Đó là một chặng đường dài, nhưng bạn đã chịu đựng! Có lẽ không phải mọi thứ đều rõ ràng. Điều này là bình thường. Khi tôi mới bắt đầu học Java, tôi không thể hiểu nổi biến tĩnh là gì. Nhưng không có vấn đề lớn. Tôi ngủ trên đó, đọc thêm một vài nguồn, và rồi sự hiểu biết đã đến. Chuẩn bị cho một cuộc phỏng vấn là một câu hỏi học thuật hơn là một câu hỏi thực tế. Do đó, trước mỗi cuộc phỏng vấn, bạn nên xem lại và ghi nhớ những điều mà bạn có thể không sử dụng thường xuyên.

Và như mọi khi, đây là một số liên kết hữu ích:

Cảm ơn tất cả các bạn đã đọc. Hẹn gặp lại bạn :) Hồ sơ GitHub của tôiTop 50 câu hỏi và câu trả lời phỏng vấn xin việc cho Java Core.  Phần 2 - 5
Bình luận
  • Phổ biến
  • Mới
Bạn phải đăng nhập để đăng nhận xet
Trang này chưa có bất kỳ bình luận nào