JavaRush /Blog Java /Random-VI /Nghỉ giải lao #112. Cách khai báo, khởi tạo và lặp lại mộ...

Nghỉ giải lao #112. Cách khai báo, khởi tạo và lặp lại một mảng trong Java. Làm cách nào để dừng một luồng Java mà không sử dụng Thread.stop()?

Xuất bản trong nhóm

Cách khai báo, khởi tạo và lặp lại một mảng trong Java

Nguồn: FreeCodeCamp Trong bài viết này chúng ta sẽ nói về mảng trong Java. Chúng ta sẽ xem xét một số ví dụ để giúp bạn hiểu mảng là gì, cách khai báo mảng và cách sử dụng mảng trong mã Java. Nghỉ giải lao #112.  Cách khai báo, khởi tạo và lặp lại một mảng trong Java.  Làm cách nào để dừng một luồng Java mà không sử dụng Thread.stop()?  - 1

Mảng là gì?

Trong Java, chúng ta sử dụng một mảng để lưu trữ nhiều giá trị của cùng một kiểu dữ liệu trong một biến duy nhất. Nó cũng có thể được coi là tập hợp các giá trị có cùng kiểu dữ liệu. Điều này có nghĩa là nếu bạn định lưu trữ các chuỗi trong mảng của mình chẳng hạn, thì tất cả các giá trị trong mảng của bạn phải là chuỗi.

Cách khai báo một mảng trong Java

Chúng ta sử dụng dấu ngoặc vuông [] để khai báo một mảng . Như thế:
String[] names;
Ở đây chúng ta đã khai báo một biến tên sẽ chứa một chuỗi các chuỗi. Nếu chúng ta cần khai báo một biến cho số nguyên (số nguyên) thì chúng ta sẽ làm như sau:
int[] myIntegers;
Vì vậy, để tạo một mảng, chúng ta chỉ định loại dữ liệu sẽ được lưu trữ trong mảng, theo sau là dấu ngoặc vuông và sau đó là tên của mảng.

Cách khởi tạo một mảng trong Java

Khởi tạo một mảng có nghĩa là gán giá trị cho mảng. Hãy khởi tạo các mảng mà chúng ta đã khai báo ở phần trước:
String[] names = {"John", "Jade", "Love", "Allen"};
int[] myIntegers = {10, 11, 12};
Chúng tôi đã khởi tạo mảng của mình bằng cách chuyển các giá trị cùng loại dữ liệu, phân tách từng giá trị bằng dấu phẩy. Nếu chúng ta muốn truy cập các phần tử/giá trị trong mảng của mình, chúng ta sẽ truy cập vào số chỉ mục của chúng trong mảng. Chỉ số của phần tử đầu tiên là 0. Dưới đây là ví dụ:
String[] names = {"John", "Jade", "Love", "Allen"};

System.out.println(names[0]);
// John

System.out.println(names[1]);
// Jade

System.out.println(names[2]);
// Love

System.out.println(names[3]);
// Allen
Bây giờ chúng ta đã biết cách truy cập từng phần tử, hãy thay đổi giá trị của phần tử thứ ba.
String[] names = {"John", "Jade", "Love", "Allen"};
names[2] = "Victor";

System.out.println(names[2]);
// Victor
Chúng ta cũng có thể kiểm tra độ dài của mảng bằng thuộc tính length . Đây là một ví dụ:
String[] names = {"John", "Jade", "Love", "Allen"};
System.out.println(names.length);
// 4

Cách lặp qua một mảng trong Java

Chúng ta có thể sử dụng vòng lặp for để lặp qua các phần tử của mảng .
String[] names = {"John", "Jade", "Love", "Allen"};
for (int i = 0; i < names.length; i++) {
  System.out.println(names[i]);
}

// John
// Jade
// Love
// Allen
Vòng lặp trên sẽ in các phần tử của mảng của chúng ta. Chúng tôi đã sử dụng thuộc tính độ dài để chỉ định số lần vòng lặp sẽ chạy.

Phần kết luận

Trong bài viết này, chúng ta đã học cách khai báo và khởi tạo mảng trong mã Java. Chúng ta cũng đã học cách truy cập từng phần tử của một mảng và cách lặp qua các phần tử đó. Chúc mừng mã hóa!

Làm cách nào để dừng một luồng Java mà không sử dụng Thread.stop()?

Nguồn: 4comprehension Nếu bạn đang đọc bài viết này thì có lẽ bạn đang tự hỏi tại sao và quan trọng nhất là làm thế nào để dừng một luồng nếu bạn không thể chỉ dựa vào phương thức Thread#stop() , phương thức này đã không được dùng nữa kể từ Java 1.2 ? Nghỉ giải lao #112.  Cách khai báo, khởi tạo và lặp lại một mảng trong Java.  Làm cách nào để dừng một luồng Java mà không sử dụng Thread.stop()?  - 2Câu trả lời ngắn gọn là bạn nên sử dụng tính năng gián đoạn vì Thread#stop không an toàn. Nhưng nếu bạn muốn hiểu tại sao, như thế nào và cái InterruptedException phiền toái đó dùng để làm gì , hãy tiếp tục đọc.

Sự cố với Thread#stop()

Cho dù nó có vẻ trực quan đến mức nào thì việc làm gián đoạn một luồng đang chạy, bắt đầu và dừng là hai trường hợp hoàn toàn khác nhau. Tại sao? Khi chúng ta bắt đầu một thread mới, chúng ta đang bắt đầu lại từ đầu, đó là trạng thái được xác định rõ ràng, nhưng khi chúng ta cố gắng dừng một thread hiện có, nó có thể xảy ra ở giữa một thao tác không thể bị gián đoạn ngay lập tức . Hãy tưởng tượng một luồng thực hiện các thay đổi đối với cấu trúc dữ liệu an toàn theo luồng. Trong khi nó đang ở giữa phần quan trọng... và sau đó luồng biến mất một cách kỳ diệu - các khóa được giải phóng và bây giờ một luồng khác có thể quan sát cấu trúc dữ liệu còn lại ở trạng thái không nhất quán. Hãy xem xét ví dụ sau về bộ đếm an toàn theo luồng với một số trạng thái bên trong:
class ThreadSafeCounter {

    private volatile int counter = 0;
    private volatile boolean dirty = false;

    public synchronized int incrementAndGet() {
        if (dirty) {
            throw new IllegalStateException("this should never happen");
        }
        dirty = true;
        // ...
        Thread.sleep(5000); // boilerplate not included
        // ...
        counter = counter + 1;
        dirty = false;
        return counter;
    }
}
Như bạn có thể thấy, phương thức getAndIncrement() tăng bộ đếm và thay đổi dirty flag . Khóa đảm bảo rằng mỗi khi một luồng đi vào getAndIncrement() cờ bẩn được đặt thành false . Bây giờ hãy tạo một chủ đề và dừng nó đột ngột:
var threadSafeCounter = new ThreadSafeCounter();

var t1 = new Thread(() -> {
    while (true) {
        threadSafeCounter.incrementAndGet();
    }
});

t1.start();
Thread.sleep(500);
t1.stop();

threadSafeCounter.incrementAndGet();

// Exception in thread "main" java.lang.IllegalStateException: this should never happen
Bây giờ, phiên bản threadSafeCounter , mặc dù được triển khai đúng cách, vẫn bị hỏng vĩnh viễn do luồng bị dừng đột ngột. Làm thế nào để khắc phục nó?

Sự gián đoạn của chủ đề hợp tác

Thay vì dừng luồng, chúng ta nên dựa vào sự gián đoạn hợp tác của luồng. Nói một cách đơn giản, chúng ta phải yêu cầu luồng tự dừng vào đúng thời điểm bằng cách sử dụng Thread#interrupt . Tuy nhiên, gọi Thread#interrupt thay vì Thread#stop là không đủ. Việc kết thúc an toàn của một luồng chỉ có thể đạt được bằng cách sử dụng ngắt luồng được chia sẻ, có nghĩa là trong luồng chúng ta cần xử lý các tín hiệu ngắt, có hai loại:
  • Cờ Boolean bị gián đoạn

  • Ngoại lệ bị gián đoạn

Xử lý cờ ngắt

Khi ở bên trong một thread, chúng ta có thể kiểm tra xem nó có bị gián đoạn hay không bằng cách gọi một trong các phương thức:
Thread.currentThread().isInterrupted(); // reads the interrupted flag
Thread.interrupted(); // reads and resets the interrupted flag
Vì không có cách chung nào để xử lý ngắt nên chúng ta cần áp dụng một hành động phù hợp với ngữ cảnh của mình. Trong ví dụ ở phần trước, luồng tăng bộ đếm của chúng ta theo một vòng lặp vô hạn. Thay vì dừng ngay lập tức, chúng ta có thể chỉ cần đợi luồng hoàn thành công việc tăng dần rồi thoát khỏi vòng lặp:
var threadSafeCounter = new ThreadSafeCounter();

var t1 = new Thread(() -> {
    while (!Thread.interrupted()) {
        threadSafeCounter.incrementAndGet();
    }
});

t1.start();
Thread.sleep(500);
t1.interrupt();

threadSafeCounter.incrementAndGet();
Bây giờ luồng kết thúc một cách an toàn vào đúng thời điểm mà không gây ra sự hỗn loạn.

Xử lý ngoại lệ bị gián đoạn

Điều gì xảy ra nếu chúng ta cố gắng làm gián đoạn một chuỗi đang chờ? Đương nhiên, chúng tôi không thể tính đến cờ gián đoạn vì luồng đang bận thực hiện thao tác chặn. Đó là lý do tại sao InterruptedException tồn tại . Đây không phải là một ngoại lệ tiêu chuẩn mà là một dạng tín hiệu ngắt khác chỉ tồn tại để xử lý các ngắt chặn! Giống như Thread#interrupted , nó đặt lại cờ gián đoạn . Hãy thay đổi lại ví dụ trên và thêm các khoảng dừng trước mỗi lần tăng. Bây giờ chúng ta cần xử lý InterruptedException do Thread#sleep ném ra :
var threadSafeCounter = new ThreadSafeCounter();

var t1 = new Thread(() -> {
    while (!Thread.interrupted()) {
        threadSafeCounter.incrementAndGet();
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            break;
        }
    }
});

t1.start();
Thread.sleep(500);
t1.interrupt();

assertThat(threadSafeCounter.incrementAndGet()).isGreaterThan(0);
Trong trường hợp của chúng tôi, chỉ cần thoát khỏi vòng lặp là đủ. Nhưng điều gì sẽ xảy ra nếu bạn không nghĩ rằng một phương thức cụ thể sẽ chịu trách nhiệm xử lý các ngắt? nếu chữ ký phương thức không thể thay đổi thì sao? Trong trường hợp này, chúng ta cần khôi phục cờ gián đoạn và/hoặc đóng gói lại ngoại lệ, ví dụ:
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    throw new RuntimeException(e);
}

Phần kết luận:

  • Thread#stop không được dùng nữa mà không có lý do - nó thực sự rất không an toàn.

  • Các luồng bị dừng do gián đoạn - tín hiệu được giải thích bởi chính luồng đó.

  • InterruptedException không phải là một ngoại lệ thông thường - đó là cách tích hợp sẵn của Java để báo hiệu rằng một luồng đã bị gián đoạn.

  • Thread.currentThread().isInterrupted()Thread.interrupted() không giống nhau. Cái đầu tiên chỉ đơn giản là đọc cờ gián đoạn và cái còn lại sẽ đặt lại nó sau đó.

  • Không có cách chung nào để xử lý các tín hiệu ngắt - “điều đó tùy thuộc vào hoàn cảnh”.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION