Tại sao chuỗi biến cục bộ lại an toàn trong Java


90

Tôi đang đọc đa luồng trong Java và tôi bắt gặp điều này

Các biến cục bộ là chuỗi an toàn trong Java.

Kể từ đó tôi đã suy nghĩ Làm thế nào / Tại sao các biến cục bộ lại an toàn cho luồng.

Ai đó có thể vui lòng cho tôi biết.


27
Vì chúng được phân bổ trong Stack. Và chủ đề không share chồng .. độc đáo của nó cho mỗi ..
Rohit Jain

Câu trả lời:


102

Khi bạn tạo một luồng, nó sẽ có ngăn xếp của riêng nó được tạo. Hai luồng sẽ có hai ngăn xếp và một luồng không bao giờ chia sẻ ngăn xếp của nó với luồng khác.

Tất cả các biến cục bộ được xác định trong chương trình của bạn sẽ được cấp phát bộ nhớ trong ngăn xếp (Như Jatin đã nhận xét, bộ nhớ ở đây có nghĩa là, giá trị tham chiếu cho các đối tượng và giá trị cho các kiểu nguyên thủy) (Mỗi lệnh gọi phương thức bởi một luồng tạo ra một khung ngăn xếp trên ngăn xếp của chính nó). Ngay sau khi quá trình thực thi phương thức được hoàn thành bởi luồng này, khung ngăn xếp sẽ bị xóa.

Có một bài giảng tuyệt vời của giáo sư Stanford trên youtube có thể giúp bạn hiểu khái niệm này.


13
Tôi xin lỗi, bạn đã nhầm, chỉ có các biến cục bộ nguyên thủy được lưu trữ trên ngăn xếp. Phần còn lại tất cả các biến được lưu trữ trên Heap. Java 7 đã giới thiệu phân tích thoát, mà đối với một số biến thể có thể phân bổ nó trong ngăn xếp
Jatin

6
Stack chỉ giữ tham chiếu đến đối tượng trên heap. Vì ngăn xếp bị xóa, nên tham chiếu cũng vậy. do đó nó có sẵn để thu gom rác
Jatin

6
@Jatin: Bạn nói đúng. Khi tôi muốn nói đến bộ nhớ, tôi có nghĩa là tham chiếu-giá trị cho các đối tượng và giá trị cho các giá trị ban đầu (tôi nghĩ rằng các nhà phát triển mới làm quen cũng biết rằng Đối tượng nằm trên heap).
kosa

2
@Nambari nhưng nếu giá trị tham chiếu trỏ đến một biến được chia sẻ. Sau đó, làm thế nào chúng ta có thể nói rằng nó là chủ đề an toàn?
H.Rabiee

3
@hajder: Điều gì làm cho một biến được chia sẻ? bắt đầu từ đó. Cả biến instance hoặc class phải không? không phải là biến cục bộ VÀ đọc câu trả lời của Marko Toplink trong chủ đề này, tôi nghĩ đó là điểm bạn đang bối rối.
kosa

19

Các biến cục bộ được lưu trữ trong ngăn xếp riêng của mỗi luồng. Điều đó có nghĩa là các biến cục bộ không bao giờ được chia sẻ giữa các luồng. Điều đó cũng có nghĩa là tất cả các biến nguyên thủy cục bộ đều an toàn.

public void someMethod(){

   long threadSafeInt = 0;

   threadSafeInt++;
}

Các tham chiếu cục bộ đến các đối tượng hơi khác một chút. Bản thân tham chiếu không được chia sẻ. Tuy nhiên, đối tượng được tham chiếu không được lưu trữ trong ngăn xếp cục bộ của mỗi luồng. Tất cả các đối tượng được lưu trữ trong heap được chia sẻ. Nếu một đối tượng được tạo cục bộ không bao giờ thoát khỏi phương thức mà nó đã được tạo trong đó, nó là luồng an toàn. Trên thực tế, bạn cũng có thể truyền nó cho các phương thức và đối tượng khác miễn là không có phương thức hoặc đối tượng nào trong số này làm cho đối tượng được truyền sẵn có cho các luồng khác


Có một sai lầm trong agrument, vui lòng xem ý kiến ​​của phản hồi @Nambari
Jatin

Nếu bạn đang chỉ ra thực tế rằng localSafeInt sẽ luôn chỉ là 0, sau đó là 1 và sau đó xóa bằng mọi cách thì tốt. Vì vậy, nó cho thấy rằng biến này không được chia sẻ giữa các chủ đề và do đó không bị ảnh hưởng bởi đa luồng .. tôi nghĩ rằng bạn có thể trỏ nó ra nhiều hơn một chút mà threadsafe luôn chỉ là 0 hoặc 1
Tobi

14

Hãy nghĩ về các phương pháp như định nghĩa về chức năng. Khi hai luồng chạy cùng một phương thức, chúng không liên quan gì đến nhau. Mỗi người sẽ tạo phiên bản riêng của từng biến cục bộ và sẽ không thể tương tác với nhau theo bất kỳ cách nào.

Nếu các biến không phải là cục bộ (như các biến cá thể được định nghĩa bên ngoài một phương thức ở cấp lớp), thì chúng sẽ được gắn vào cá thể (không phải cho một lần chạy phương thức). Trong trường hợp này, hai luồng chạy cùng một phương thức đều thấy một biến và điều này không an toàn cho luồng.

Hãy xem xét hai trường hợp sau:

public class NotThreadsafe {
    int x = 0;
    public int incrementX() {
        x++;
        return x;
    }
}

public class Threadsafe {
    public int getTwoTimesTwo() {
        int x = 1;
        x++;
        return x*x;
    }
}

Trong lần đầu tiên, hai luồng chạy trên cùng một phiên bản của NotThreadsafesẽ thấy cùng một dấu x. Điều này có thể nguy hiểm, bởi vì các chủ đề đang cố gắng thay đổi x! Trong thứ hai, hai luồng chạy trên cùng một phiên bản của Threadsafesẽ thấy các biến hoàn toàn khác nhau và không thể ảnh hưởng lẫn nhau.


6

Mỗi lệnh gọi phương thức có các biến cục bộ của riêng nó và hiển nhiên, một lệnh gọi phương thức xảy ra trong một luồng duy nhất. Một biến chỉ được cập nhật bởi một luồng duy nhất vốn dĩ là một luồng an toàn.

Tuy nhiên , hãy theo dõi kỹ ý nghĩa chính xác của điều này: chỉ các bản ghi vào chính biến là an toàn luồng; gọi các phương thức trên đối tượng mà nó tham chiếu đến vốn dĩ không an toàn theo luồng . Tương tự với việc cập nhật trực tiếp các biến của đối tượng.


1
Bạn nói "các phương thức gọi trên đối tượng mà nó tham chiếu đến vốn không an toàn cho luồng". Nhưng, làm thế nào đối tượng được tham chiếu cục bộ phương thức - khởi tạo trong phạm vi phương thức này - có thể được chia sẻ bởi hai luồng? Bạn có thể chỉ ra bằng ví dụ?
Akshay Lokur

1
Một biến cục bộ có thể có hoặc không chứa một đối tượng được khởi tạo trong phạm vi phương thức, đó không phải là một phần của câu hỏi. Ngay cả khi có, phương thức có thể truy cập trạng thái được chia sẻ.
Marko Topolnik

6

Ngoài những câu trả lời khác như của Nambari.

Tôi muốn chỉ ra rằng bạn có thể sử dụng một biến cục bộ trong một phương thức kiểu ẩn danh:

Phương thức này có thể được gọi trong các luồng khác có thể ảnh hưởng đến sự an toàn của luồng, vì vậy java buộc tất cả các biến cục bộ được sử dụng trong các kiểu không rõ ràng phải được khai báo là cuối cùng.

Hãy xem xét mã bất hợp pháp này:

public void nonCompilableMethod() {
    int i=0;
    for(int t=0; t<100; t++)
    {
      new Thread(new Runnable() {
                    public void run() {
                      i++; //compile error, i must be final:
                      //Cannot refer to a non-final variable i inside an
                      //inner class defined in a different method
                    }
       }).start();
     }
  }

Nếu java đã cho phép điều này (như C # thực hiện thông qua "bao đóng"), thì một biến cục bộ sẽ không còn là luồng an toàn trong mọi trường hợp. Trong trường hợp này, giá trị của iở cuối tất cả các luồng không được đảm bảo 100.


Xin chào Weston, Từ cuộc thảo luận ở trên và các câu trả lời bên dưới, tôi hiểu rằng java đảm bảo an toàn luồng cho tất cả các biến cục bộ. Sau đó, tôi có thể biết việc sử dụng thực tế của từ khóa được đồng bộ hóa là gì không? bạn có thể vui lòng giải thích với một ví dụ như thế này.
Prabhu

5

Chủ đề sẽ có ngăn xếp riêng của nó. Hai luồng sẽ có hai ngăn xếp và một luồng không bao giờ chia sẻ ngăn xếp của nó với luồng khác. Các biến cục bộ được lưu trữ trong ngăn xếp riêng của mỗi luồng. Điều đó có nghĩa là các biến cục bộ không bao giờ được chia sẻ giữa các luồng.


3

Về cơ bản có bốn loại lưu trữ trong java để lưu trữ thông tin và dữ liệu Lớp:

Vùng phương pháp, đống, ngăn xếp JAVA, PC

vì vậy vùng Phương thức và Heap được chia sẻ bởi tất cả các luồng nhưng mọi luồng đều có Ngăn xếp và PC JAVA của riêng nó và nó không được chia sẻ bởi bất kỳ Chủ đề nào khác.

Mỗi phương thức trong java là khung Stack. vì vậy, khi một phương thức được gọi bởi một luồng mà khung ngăn xếp được tải trên Ngăn xếp JAVA của nó. Tất cả các biến cục bộ có trong khung ngăn xếp đó và ngăn xếp toán hạng liên quan sẽ không được chia sẻ bởi những người khác. PC sẽ có thông tin về lệnh tiếp theo để thực thi trong mã byte của phương thức. vì vậy tất cả các biến cục bộ đều THREAD AN TOÀN.

@Weston cũng đã đưa ra câu trả lời tốt.


1

Chỉ các biến cục bộ được lưu trữ trên ngăn xếp luồng.

Biến cục bộ đó là primitive type(ví dụ int, long ...) được lưu trữ trên thread stackvà kết quả là - chủ đề khác không có quyền truy cập vào nó.

Biến cục bộreference type(kế thừa của Object) chứa từ 2 phần - địa chỉ (được lưu trên thread stack) và đối tượng (được lưu trên heap)


class MyRunnable implements Runnable() {
    public void run() {
        method1();
    }

    void method1() {
        int intPrimitive = 1;

        method2();
    }

    void method2() {
        MyObject1 myObject1 = new MyObject1();
    }
}

class MyObject1 {
    MyObject2 myObject2 = new MyObject2();
}

class MyObject2 {
    MyObject3 myObject3 = MyObject3.shared;
}

class MyObject3 {
    static MyObject3 shared = new MyObject3();

    boolean b = false;
}

nhập mô tả hình ảnh ở đây

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.