Hành vi của phương thức tĩnh trong môi trường đa luồng trong java


114

Có một câu hỏi ngu ngốc đơn giản khiến tôi bận tâm và nảy ra nhiều lập luận trong đầu. Tôi muốn loại bỏ tất cả những nghi ngờ về những câu hỏi dưới đây.

class Clstest{

    public static String testStaticMethod(String inFileStr) {

        // section 0

        // section 1

        // do something with inFileStr

        // section 2

        // section 3

        return inFileStr;

    }

}

Giả sử rằng có năm luồng đang thực hiện lệnh gọi đến Clstest.testStaticMethod("arg-n")cùng một lúc.

Chủ đề 1 cuộc gọi Clstest.testStaticMethod("arg-1").

Khi luồng 1 nằm trong phần 1, luồng 2 sẽ gọi Clstest.testStaticMethod("arg-2").

Sau đó, điều gì sẽ xảy ra với Chủ đề 1? Nó sẽ chuyển sang trạng thái ngủ?

Khi Chủ đề 1 có cơ hội, nó sẽ tiếp tục thực thi từ phần 1, nơi nó đã bị tạm dừng?

Làm thế nào nó xảy ra khi có một Clstest.testStaticMethodvà cùng một Clstest.testStaticMethodđược chia sẻ giữa tất cả năm chủ đề?

Có bất kỳ khả năng nào để trao đổi inFileStrgửi bởi nhiều chủ đề không?


Bạn đang nhắm mục tiêu ngôn ngữ nào?
ΩmegaMan

3
@ OmegaMan: đó là java
namalfernandolk

Câu trả lời:


192

Câu trả lời của Hans Passant là tốt. Nhưng tôi nghĩ rằng tôi sẽ thử và giải thích ở mức độ đơn giản hơn một chút cho bất kỳ ai bắt gặp điều này và chưa quen với Java. Đây rồi ..

Bộ nhớ trong java được chia thành hai loại - heap và stack. Vùng đống là nơi tất cả các đối tượng sống và các ngăn xếp là nơi các luồng thực hiện công việc của chúng. Mỗi luồng có ngăn xếp riêng và không thể truy cập các ngăn xếp khác. Mỗi luồng cũng có một con trỏ vào mã trỏ đến bit mã mà chúng hiện đang chạy.

Khi một luồng bắt đầu chạy một phương thức mới, nó sẽ lưu các đối số và biến cục bộ trong phương thức đó trên ngăn xếp của chính nó. Một số giá trị này có thể là con trỏ đến các đối tượng trên heap. Nếu hai luồng đang chạy cùng một phương thức tại cùng một thời điểm, cả hai sẽ có con trỏ mã trỏ đến phương thức đó và có các bản sao của đối số và biến cục bộ trên ngăn xếp của chúng. Chúng sẽ chỉ gây trở ngại cho nhau nếu những thứ trên ngăn xếp của chúng trỏ đến những đối tượng giống nhau trên đống. Trong trường hợp đó, tất cả các loại điều có thể xảy ra. Nhưng như Hans đã chỉ ra, các Chuỗi là bất biến (không thể thay đổi) nên chúng tôi an toàn nếu đây là đối tượng duy nhất được "chia sẻ".

Vì vậy, nhiều chủ đề có thể chạy cùng một phương pháp. Chúng có thể không chạy cùng một lúc - điều đó phụ thuộc vào số lõi bạn có trên máy của mình khi JVM ánh xạ các luồng Java với các luồng OS, được lập lịch trên các luồng phần cứng. Do đó, bạn có rất ít quyền kiểm soát cách các luồng này xen kẽ nhau mà không sử dụng các cơ chế đồng bộ hóa phức tạp .

Lưu ý rằng ngủ là một cái gì đó mà một chuỗi làm với chính nó.


3
Vì vậy, trong môi trường bộ xử lý đa lõi có thể có nhiều luồng chạy cùng một đoạn mã cùng một lúc phải không? Và trong môi trường bộ xử lý đơn chỉ có một luồng chạy tại một thời điểm nhất định. (nhiều luồng chia sẻ thời gian giữa chúng.) Vì vậy, khi lịch trình luồng cho cơ hội từ luồng kích thích hiện tại (A) sang luồng (B), thì luồng (A) sẽ tiếp tục từ nơi nó bị tạm dừng như thế nào? Ý tôi là làm sao nó biết được điểm sơ yếu lý lịch? Có phải vì "Mỗi luồng cũng có một con trỏ vào mã trỏ đến bit mã mà chúng hiện đang chạy không?" như bạn đã nói?
namalfernandolk

6
Bạn đã có nó. Chỉ để làm rõ một số điểm - trước tiên, cách các luồng được lập lịch nằm ngoài tầm kiểm soát của Java. Tôi đang nói về Hotspot JVM của Sun ở đây. JVM ánh xạ một luồng Java với một luồng OS và OS quyết định luồng nào sẽ chạy. Như bạn nói, trên một máy lõi đơn, hệ điều hành chỉ có thể chạy một lõi tại một thời điểm, nhưng trong máy đa lõi, nó có thể chạy nhiều hơn một lõi cùng một lúc. Thứ hai, một luồng không thực sự biết khi nào nó bị tạm dừng, thông tin duy nhất mà nó có là con trỏ chương trình (con trỏ vào mã) và ngăn xếp, được lưu và khôi phục chính xác như ban đầu.
selig

Vì vậy, can thiệp giữa các luồng xảy ra khi các luồng đang sử dụng các biến ngoài phạm vi cục bộ của chúng và ví dụ: một luồng cập nhật giá trị của biến trước khi luồng khác kéo biến đó (hoặc con trỏ đến biến) trên ngăn xếp của chính nó? Đó có phải là một cách hiểu đúng?
hariszhr

2
Nói chính xác hơn một chút ... can thiệp chỉ có thể xảy ra và có thể xảy ra nhưng không nhất thiết sẽ xảy ra ... khi các luồng chia sẻ mọi thứ trên heap (không cục bộ). Có một số cách khác nhau có thể xảy ra nhiễu, tùy thuộc vào sự phụ thuộc giữa các phần khác nhau của mã. Tôi không chắc về ví dụ của bạn vì còn thiếu một số chi tiết. Có lẽ bạn đang đề cập đến một vấn đề tiềm ẩn trong đó các luồng A và B đều đọc một giá trị được chia sẻ và sau đó cả hai đều cập nhật nó dựa trên giá trị đã đọc. Đây là một cuộc đua dữ liệu.
selig

1
@selig nếu tôi có một lớp chỉ có các phương thức thể hiện cho ví dụ: một lớp dịch vụ và Nó là singleton thì tôi không cần phải lo lắng về việc nhiều luồng thực thi các phương thức cá thể cùng một lúc vì lớp dịch vụ không giữ bất kỳ trạng thái nào. chỉ hiện diện nếu lớp có các biến cá thể. Tôi hiểu có đúng không?
Yug Singh

67

Nó sẽ chuyển sang trạng thái ngủ?

Không, việc chạy một luồng không ảnh hưởng đến các luồng khác miễn là chúng không cố ý đồng bộ hóa với nhau. Nếu bạn có nhiều hơn một lõi bộ xử lý, tất cả các máy gần đây đều có, các luồng đó có khả năng thực thi chính xác cùng một lúc. Điều đó sẽ ít xảy ra hơn khi bạn bắt đầu 5 luồng vì máy của bạn có thể không có đủ lõi. Hệ điều hành buộc phải lựa chọn giữa chúng, cho phép chúng chạy một khoảng thời gian. Công việc của bộ lập lịch luồng. Khi đó, một luồng sẽ không ở trạng thái "ngủ", nó chỉ đơn giản là tạm dừng và chờ bộ lập lịch luồng cho nó cơ hội chạy. Nó sẽ tiếp tục ở nơi nó bị gián đoạn bởi bộ lập lịch.

Có khả năng nào để trao đổi inFileStr được gửi bởi nhiều luồng không?

Không có khả năng như vậy, các luồng có ngăn xếp riêng của chúng vì vậy bất kỳ đối số phương thức và biến cục bộ nào sẽ là duy nhất cho mỗi luồng. Hơn nữa, việc sử dụng một chuỗi đảm bảo rằng các chuỗi này không thể can thiệp vào nhau vì các chuỗi là bất biến.

Không có gì đảm bảo như vậy nếu đối số là một tham chiếu đến một loại đối tượng có thể thay đổi khác. Hoặc nếu bản thân phương thức sử dụng các biến tĩnh hoặc tham chiếu đến các đối tượng trên heap. Đồng bộ hóa là bắt buộc khi một luồng sửa đổi đối tượng và một luồng khác đọc nó. Từ khóa lock trong ngôn ngữ C # là cách viết sẵn để thực hiện đồng bộ hóa cần thiết. Thực tế là phương thức là tĩnh không có nghĩa là không bao giờ cần phải đồng bộ hóa như vậy. Khả năng xảy ra ít hơn vì bạn không phải lo lắng về các luồng truy cập vào cùng một đối tượng (chia sẻ điều này ).


3
Rất tiếc, chưa bao giờ thấy thẻ [java]. Đủ gần.
Hans Passant

Tôi đã quên thêm khi nó được đăng. Lỗi của tôi. :). Bất kỳ cách nào cảm ơn bạn cho câu trả lời. Nó rất hữu ích.
namalfernandolk
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.