Sự khác biệt giữa bế tắc và livelock là gì?


Câu trả lời:


398

Lấy từ http://en.wikipedia.org/wiki/Deadlock :

Trong điện toán đồng thời, bế tắc là trạng thái trong đó mỗi thành viên của một nhóm hành động, đang chờ một thành viên khác phát hành khóa

Một livelock tương tự như một bế tắc, ngoại trừ việc các trạng thái của các quá trình liên quan đến livelock liên tục thay đổi liên quan đến nhau, không tiến triển. Livelock là một trường hợp đặc biệt của nạn đói tài nguyên; định nghĩa chung chỉ nói rằng một quá trình cụ thể không tiến triển.

Một ví dụ thực tế về sự sống động xảy ra khi hai người gặp nhau trong một hành lang hẹp và mỗi người cố gắng lịch sự bằng cách di chuyển sang một bên để vượt qua người kia, nhưng cuối cùng họ vẫn lắc lư từ bên này sang bên kia mà không tiến triển gì vì cả hai liên tục di chuyển cùng một cách cùng một lúc.

Livelock là một rủi ro với một số thuật toán phát hiện và phục hồi từ bế tắc. Nếu có nhiều quá trình thực hiện hành động, thuật toán phát hiện khóa chết có thể được kích hoạt nhiều lần. Điều này có thể tránh được bằng cách đảm bảo rằng chỉ một quá trình (được chọn ngẫu nhiên hoặc ưu tiên) thực hiện hành động.


8
Tôi đã tìm thấy nó rồi, nhưng họ không có ví dụ ở đó như bạn có thể thấy, dù sao cũng cảm ơn
macindows

61
Tôi sẽ không cung cấp một ví dụ mã, nhưng hãy xem xét hai quy trình, mỗi quy trình đang chờ một tài nguyên mà tài nguyên kia có nhưng chờ theo cách không chặn. Khi mỗi người học họ không thể tiếp tục, họ giải phóng tài nguyên bị giữ của mình và ngủ trong 30 giây, sau đó họ lấy lại tài nguyên ban đầu của mình, sau đó cố gắng lấy tài nguyên mà quá trình khác nắm giữ, sau đó rời đi, sau đó lấy lại. Vì cả hai quá trình đang cố gắng đối phó (chỉ là tồi tệ), đây là một bản nhạc sống.
mah

4
Bạn có thể cho tôi ví dụ tương tự nhưng với sự bế tắc, cảm ơn trước
macindows

32
Một ví dụ bế tắc dễ dàng hơn nhiều ... giả sử hai quy trình A và B, và mỗi quy trình muốn tài nguyên r1 và tài nguyên r2. Giả sử A nhận (hoặc đã có) r1 và B nhận (hoặc đã có) r2. Bây giờ mỗi người cố gắng để có được tài nguyên mà người kia có, mà không có thời gian chờ. A bị chặn vì B giữ r2 và B bị chặn vì A giữ r1. Mỗi quy trình bị chặn và do đó không thể giải phóng tài nguyên mà người khác muốn, gây ra bế tắc.
mah

2
Trong bối cảnh của Bộ nhớ giao dịch, có một video tuyệt vời thể hiện sự bế tắc và livelock: youtube.com/watch?v=_IxsOEEzf-c
BlackV Donable

78

Livelock

Một chủ đề thường hành động để đáp ứng với hành động của một chủ đề khác. Nếu hành động của luồng khác cũng là phản hồi đối với hành động của luồng khác, thì có thể kết quả là livelock.

Như với sự bế tắc, các chủ đề được nối lại không thể tiến bộ hơn nữa . Tuy nhiên, các chủ đề không bị chặn - đơn giản là chúng quá bận rộn để trả lời cho nhau để tiếp tục công việc . Điều này có thể so sánh với hai người cố gắng vượt qua nhau trong một hành lang: Alphonse di chuyển sang trái để Gaston vượt qua, trong khi Gaston di chuyển sang phải để cho Alphonse vượt qua. Thấy rằng họ vẫn đang chặn nhau, Alphonse di chuyển sang phải, trong khi Gaston di chuyển sang trái. Họ vẫn đang chặn nhau, và cứ thế ...

Sự khác biệt chính giữa livelockbế tắc là các luồng sẽ không bị chặn, thay vào đó chúng sẽ cố gắng phản hồi với nhau liên tục.

Trong hình ảnh này, cả hai vòng tròn (chủ đề hoặc quy trình) sẽ cố gắng cung cấp không gian cho người khác bằng cách di chuyển sang trái và phải. Nhưng họ không thể di chuyển thêm nữa.

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


Các ví dụ về mã cho các bản nhạc gốc stackoverflow.com/questions/1036364/good-example-of-leelsock
Yauhen Yakimovich

1
Điều này có một cái tên. Một từ lóng có lẽ, nhưng vẫn: schlumperdink : P
John Red

64

Tất cả nội dung và ví dụ ở đây là từ

Hệ điều hành: Nguyên tắc thiết kế và nội bộ
William Stallings
Phiên bản 8º

Bế tắc : Một tình huống trong đó hai hoặc nhiều quá trình không thể tiến hành vì mỗi quá trình đang chờ một người khác làm một việc gì đó.

Ví dụ, hãy xem xét hai quy trình, P1 và P2 và hai tài nguyên, R1 và R2. Giả sử rằng mỗi quá trình cần truy cập vào cả hai tài nguyên để thực hiện một phần chức năng của nó. Sau đó, có thể có tình huống sau: HĐH gán R1 cho P2 và R2 cho P1. Mỗi quá trình đang chờ một trong hai tài nguyên. Cả hai sẽ không giải phóng tài nguyên mà nó đã sở hữu cho đến khi nó có được tài nguyên khác và thực hiện chức năng yêu cầu cả hai tài nguyên. Hai quá trình bị bế tắc

Livelock : Một tình huống trong đó hai hoặc nhiều quá trình liên tục thay đổi trạng thái để đáp ứng với những thay đổi trong (các) quy trình khác mà không thực hiện bất kỳ công việc hữu ích nào:

Đói : Một tình huống trong đó một quá trình có thể chạy được bỏ qua vô thời hạn bởi bộ lập lịch; mặc dù nó có thể tiến hành, nó không bao giờ được chọn.

Giả sử rằng ba quy trình (P1, P2, P3) đều yêu cầu quyền truy cập định kỳ vào tài nguyên R. Hãy xem xét tình huống mà P1 đang sở hữu tài nguyên và cả P2 và P3 đều bị trì hoãn, chờ tài nguyên đó. Khi P1 thoát khỏi phần quan trọng của nó, P2 hoặc P3 sẽ được phép truy cập vào R. Giả sử rằng HĐH cấp quyền truy cập cho P3 và P1 lại yêu cầu quyền truy cập trước khi P3 hoàn thành phần quan trọng của nó. Nếu HĐH cấp quyền truy cập vào P1 sau khi P3 kết thúc và sau đó luân phiên cấp quyền truy cập vào P1 và P3, thì P2 có thể bị từ chối truy cập vào tài nguyên vô thời hạn, mặc dù không có tình huống bế tắc.

PHỤ LỤC A - CHỦ ĐỀ TRONG Ý TƯỞNG

Ví dụ bế tắc

Nếu cả hai quá trình đều đặt cờ của chúng thành true trước khi thực thi câu lệnh while, thì mỗi cái sẽ nghĩ rằng cái kia đã vào phần quan trọng của nó, gây ra bế tắc.

/* PROCESS 0 */
flag[0] = true;            // <- get lock 0
while (flag[1])            // <- is lock 1 free?
    /* do nothing */;      // <- no? so I wait 1 second, for example
                           // and test again.
                           // on more sophisticated setups we can ask
                           // to be woken when lock 1 is freed
/* critical section*/;     // <- do what we need (this will never happen)
flag[0] = false;           // <- releasing our lock

 /* PROCESS 1 */
flag[1] = true;
while (flag[0])
    /* do nothing */;
/* critical section*/;
flag[1] = false;

Ví dụ về Livelock

/* PROCESS 0 */
flag[0] = true;          // <- get lock 0
while (flag[1]){         
    flag[0] = false;     // <- instead of sleeping, we do useless work
                         //    needed by the lock mechanism
    /*delay */;          // <- wait for a second
    flag[0] = true;      // <- and restart useless work again.
}
/*critical section*/;    // <- do what we need (this will never happen)
flag[0] = false; 

/* PROCESS 1 */
flag[1] = true;
while (flag[0]) {
    flag[1] = false;
    /*delay */;
    flag[1] = true;
}
/* critical section*/;
flag[1] = false;

[...] Xem xét chuỗi sự kiện sau:

  • P0 đặt cờ [0] thành đúng.
  • P1 đặt cờ [1] thành đúng.
  • Cờ kiểm tra P0 [1].
  • Cờ kiểm tra P1 [0].
  • P0 đặt cờ [0] thành false.
  • P1 đặt cờ [1] thành false.
  • P0 đặt cờ [0] thành đúng.
  • P1 đặt cờ [1] thành đúng.

Trình tự này có thể được kéo dài vô tận và không quá trình nào có thể vào phần quan trọng của nó. Nói đúng ra, đây không phảibế tắc , bởi vì bất kỳ sự thay đổi nào về tốc độ tương đối của hai quá trình sẽ phá vỡ chu trình này và cho phép một người bước vào phần quan trọng. Điều kiện này được gọi là livelock . Hãy nhớ lại rằng bế tắc xảy ra khi một tập hợp các quy trình muốn vào các phần quan trọng của chúng nhưng không có quá trình nào có thể thành công. Với livelock , có thể có các chuỗi thực thi thành công, nhưng cũng có thể mô tả một hoặc nhiều chuỗi thực thi trong đó không có quá trình nào đi vào phần quan trọng của nó.

Không còn nội dung từ cuốn sách nữa.

Và những gì về spinlocks?

Spinlock là một kỹ thuật để tránh chi phí của cơ chế khóa hệ điều hành. Thông thường bạn sẽ làm:

try
{
   lock = beginLock();
   doSomething();
}
finally
{
   endLock();
}

Một vấn đề bắt đầu xuất hiện khi beginLock()chi phí nhiều hơn doSomething(). Nói một cách rất hào hứng, hãy tưởng tượng điều gì xảy ra khi beginLockchi phí 1 giây, nhưng doSomethingchỉ tốn 1 mili giây.

Trong trường hợp này nếu bạn đợi 1 mili giây, bạn sẽ tránh bị cản trở trong 1 giây.

Tại sao beginLockchi phí rất nhiều? Nếu khóa miễn phí thì không tốn nhiều tiền (xem https://stackoverflow.com/a/49712993/5397116 ), nhưng nếu khóa không miễn phí, HĐH sẽ "đóng băng" luồng của bạn, thiết lập cơ chế để đánh thức bạn khi khóa được giải phóng, và sau đó đánh thức bạn một lần nữa trong tương lai.

Tất cả điều này là đắt hơn nhiều so với một số vòng kiểm tra khóa. Đó là lý do tại sao đôi khi tốt hơn để làm một "spinlock".

Ví dụ:

void beginSpinLock(lock)
{
   if(lock) loopFor(1 milliseconds);
   else 
   {
     lock = true;
     return;
   }

   if(lock) loopFor(2 milliseconds);
   else 
   {
     lock = true;
     return;
   }

   // important is that the part above never 
   // cause the thread to sleep.
   // It is "burning" the time slice of this thread.
   // Hopefully for good.

   // some implementations fallback to OS lock mechanism
   // after a few tries
   if(lock) return beginLock(lock);
   else 
   {
     lock = true;
     return;
   }
}

Nếu việc triển khai của bạn không cẩn thận, bạn có thể rơi vào livelock, dành tất cả CPU cho cơ chế khóa.

Cũng thấy:

https://preshing.com/20120226/roll-your-own-lightgra-mutex/ Việc
triển khai khóa xoay của tôi có đúng và tối ưu không?

Tóm tắt :

Bế tắc : tình huống không ai tiến bộ, không làm gì cả (ngủ, chờ đợi, v.v.). Việc sử dụng CPU sẽ thấp;

Livelock : tình huống không có ai tiến triển, nhưng CPU được dành cho cơ chế khóa chứ không phải cho tính toán của bạn;

Đói: tình huống mà một viện trưởng không bao giờ có cơ hội để chạy; bởi sự xui xẻo thuần túy hoặc bởi một số tài sản của nó (ví dụ mức độ ưu tiên thấp);

Spinlock : kỹ thuật tránh chi phí chờ khóa được giải phóng.


Thưa ông, ví dụ bạn đưa ra cho Deadlock thực sự là một ví dụ về Spinlock. Bế tắc xảy ra khi một tập hợp các quy trình bị chặn không ở trạng thái sẵn sàng hoặc đang chạy và chờ một số tài nguyên. Nhưng trong ví dụ của chúng tôi, mỗi người đang thực hiện một số nhiệm vụ, nghĩa là kiểm tra điều kiện nhiều lần. Đúng nếu tôi đã sai lầm.
Vinay Yadav

Ví dụ rất nhỏ đến mức có cơ hội mở cho cách giải thích này, vì vậy tôi đã cải thiện nó để rõ ràng hơn một chút về sự khác biệt của chúng. Mong rằng sẽ giúp.
Daniel Frederico Lins Leite

Cảm ơn bạn đã thêm về spinlocks, theo bạn spinlocks là một kỹ thuật và bạn cũng biện minh cho nó và tôi hiểu. Nhưng vấn đề đảo ngược ưu tiên đó là gì khi một quá trình P1 nằm trong Phần quan trọng và quy trình ưu tiên cao khác P2 được lên lịch trước P1 thì trong trường hợp này CPU là với P2 và cơ chế đồng bộ hóa của chúng tôi là với P1. Đây được gọi là Spinlock vì P1 ở trạng thái sẵn sàng và P2 ở trạng thái chạy . Ở đây spinlock là một vấn đề. Tôi có nhận được những thứ phải không? Tôi không thể hiểu đúng. Xin hãy giúp đỡ
Vinay Yadav

Đề nghị của tôi cho bạn là tạo ra một câu hỏi khác nêu rõ vấn đề của bạn rõ ràng hơn. Bây giờ, nếu bạn đang ở trong "không gian người dùng" và P1 đang ở trong một phiên quan trọng được bảo vệ bằng SpinLock được triển khai với một vòng lặp vô hạn và được ưu tiên; sau đó P2 sẽ cố gắng nhập nó, sẽ thất bại và sẽ đốt cháy tất cả các lát cắt thời gian của nó. Bạn đã tạo một livelock (một CPU sẽ ở mức 100%). (một cách sử dụng không tốt sẽ là bảo vệ IO đồng bộ hóa với spinlock. Bạn có thể dễ dàng thử ví dụ này) Trên "không gian kernel" có thể ghi chú này có thể giúp bạn: lxr.linux.no/linux+v3.6.6/Documentation/
Daniel Frederico Lins Leite

Cảm ơn bạn rất nhiều vì đã làm rõ. Dù sao, câu trả lời của bạn khá mô tả và hữu ích không giống như những người khác
Vinay Yadav

13

CHẾT Bế tắc là một điều kiện trong đó một nhiệm vụ chờ đợi vô thời hạn đối với các điều kiện không bao giờ có thể thỏa mãn - nhiệm vụ yêu cầu quyền kiểm soát độc quyền đối với tài nguyên được chia sẻ - nhiệm vụ giữ tài nguyên trong khi chờ các tài nguyên khác được giải phóng - các nhiệm vụ không thể buộc phải hủy tài nguyên - chờ đợi thông tư điều kiện tồn tại

LIVELOCK Điều kiện Livelock có thể phát sinh khi hai hoặc nhiều tác vụ phụ thuộc và sử dụng một số tài nguyên gây ra tình trạng phụ thuộc vòng tròn trong đó các tác vụ đó tiếp tục chạy mãi mãi, do đó chặn tất cả các tác vụ mức độ ưu tiên thấp hơn chạy (các tác vụ ưu tiên thấp hơn này gặp phải tình trạng gọi là chết đói)


Nếu các tác vụ 'được kết nối lại' tuân theo các giao thức trọng tài tài nguyên bao gồm độ trễ 'backoff' và dành phần lớn thời gian của chúng ở trạng thái ngủ, thì các tác vụ khác sẽ không bị bỏ đói.
greggo

8

Có thể hai ví dụ này minh họa cho bạn sự khác biệt giữa bế tắc và livelock:


Ví dụ Java cho một bế tắc:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class DeadlockSample {

    private static final Lock lock1 = new ReentrantLock(true);
    private static final Lock lock2 = new ReentrantLock(true);

    public static void main(String[] args) {
        Thread threadA = new Thread(DeadlockSample::doA,"Thread A");
        Thread threadB = new Thread(DeadlockSample::doB,"Thread B");
        threadA.start();
        threadB.start();
    }

    public static void doA() {
        System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
        lock1.lock();
        System.out.println(Thread.currentThread().getName() + " : holds lock 1");

        try {
            System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
            lock2.lock();
            System.out.println(Thread.currentThread().getName() + " : holds lock 2");

            try {
                System.out.println(Thread.currentThread().getName() + " : critical section of doA()");
            } finally {
                lock2.unlock();
                System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
            }
        } finally {
            lock1.unlock();
            System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
        }
    }

    public static void doB() {
        System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
        lock2.lock();
        System.out.println(Thread.currentThread().getName() + " : holds lock 2");

        try {
            System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
            lock1.lock();
            System.out.println(Thread.currentThread().getName() + " : holds lock 1");

            try {
                System.out.println(Thread.currentThread().getName() + " : critical section of doB()");
            } finally {
                lock1.unlock();
                System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
            }
        } finally {
            lock2.unlock();
            System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
        }
    }
}

Đầu ra mẫu:

Thread A : waits for lock 1
Thread B : waits for lock 2
Thread A : holds lock 1
Thread B : holds lock 2
Thread B : waits for lock 1
Thread A : waits for lock 2

Java-Ví dụ cho một livelock:


import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LivelockSample {

    private static final Lock lock1 = new ReentrantLock(true);
    private static final Lock lock2 = new ReentrantLock(true);

    public static void main(String[] args) {
        Thread threadA = new Thread(LivelockSample::doA, "Thread A");
        Thread threadB = new Thread(LivelockSample::doB, "Thread B");
        threadA.start();
        threadB.start();
    }

    public static void doA() {
        try {
            while (!lock1.tryLock()) {
                System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
                Thread.sleep(100);
            }
            System.out.println(Thread.currentThread().getName() + " : holds lock 1");

            try {
                while (!lock2.tryLock()) {
                    System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
                    Thread.sleep(100);
                }
                System.out.println(Thread.currentThread().getName() + " : holds lock 2");

                try {
                    System.out.println(Thread.currentThread().getName() + " : critical section of doA()");
                } finally {
                    lock2.unlock();
                    System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
                }
            } finally {
                lock1.unlock();
                System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
            }
        } catch (InterruptedException e) {
            // can be ignored here for this sample
        }
    }

    public static void doB() {
        try {
            while (!lock2.tryLock()) {
                System.out.println(Thread.currentThread().getName() + " : waits for lock 2");
                Thread.sleep(100);
            }
            System.out.println(Thread.currentThread().getName() + " : holds lock 2");

            try {
                while (!lock1.tryLock()) {
                    System.out.println(Thread.currentThread().getName() + " : waits for lock 1");
                    Thread.sleep(100);
                }
                System.out.println(Thread.currentThread().getName() + " : holds lock 1");

                try {
                    System.out.println(Thread.currentThread().getName() + " : critical section of doB()");
                } finally {
                    lock1.unlock();
                    System.out.println(Thread.currentThread().getName() + " : does not hold lock 1 any longer");
                }
            } finally {
                lock2.unlock();
                System.out.println(Thread.currentThread().getName() + " : does not hold lock 2 any longer");
            }
        } catch (InterruptedException e) {
            // can be ignored here for this sample
        }
    }
}

Đầu ra mẫu:

Thread B : holds lock 2
Thread A : holds lock 1
Thread A : waits for lock 2
Thread B : waits for lock 1
Thread B : waits for lock 1
Thread A : waits for lock 2
Thread A : waits for lock 2
Thread B : waits for lock 1
Thread B : waits for lock 1
Thread A : waits for lock 2
Thread A : waits for lock 2
Thread B : waits for lock 1
...

Cả hai ví dụ đều buộc các luồng xử lý các khóa theo các thứ tự khác nhau. Trong khi bế tắc chờ đợi khóa khác, thì livelock không thực sự chờ đợi - nó tuyệt vọng cố gắng để có được khóa mà không có cơ hội lấy được khóa. Mỗi lần thử đều tiêu tốn chu kỳ CPU.


Mã này là tốt đẹp. Nhưng ví dụ khóa trực tiếp là không tốt. Cho dù một luồng bị chặn trên một giá trị hoặc đang bỏ phiếu cho một thay đổi về giá trị không khác nhau về mặt khái niệm. Một thay đổi dễ dàng để minh họa rõ hơn về khóa trực tiếp là có các luồng A và B giải phóng các khóa mà họ có khi họ nhận ra rằng họ không thể có được khóa thứ hai mà họ cần. Sau đó, họ ngủ trong một giây, hỏi lại khóa ban đầu họ có, sau đó ngủ thêm một giây và cố gắng lấy lại khóa khác. Vì vậy, chu kỳ cho mỗi chu kỳ sẽ là: 1) mua lại của tôi, 2) ngủ, 3) cố gắng để có được cái khác & thất bại, 4) phát hành của tôi, 5) ngủ, 6) Lặp lại.
CognizantApe

1
Tôi nghi ngờ liệu các khóa trực tiếp mà bạn nghĩ có thực sự tồn tại đủ lâu để chúng gây rắc rối hay không. Khi bạn luôn từ bỏ tất cả các khóa bạn giữ khi bạn không thể phân bổ khóa tiếp theo, điều kiện bế tắc (và khóa trực tiếp) sẽ bị thiếu vì thực sự không còn chờ đợi nữa. ( en.wikipedia.org/wiki/Deadlock )
mmirwaldt

thực sự tình trạng khóa chết bị thiếu vì đây là những khóa trực tiếp mà chúng ta đang thảo luận. Ví dụ tôi đưa ra tương tự như ví dụ về hành lang tiêu chuẩn được đưa ra: geekforgeek.org/deadlock-starvation-and-leelsock , en.wikibooks.org/wiki/Operating_System_Design/Concurrency/iêu , docs.oracle.com/javase/tutorial/essential / concurrency / khắc
CognizantApe

0

Hãy tưởng tượng bạn đã xử lý chủ đề A và chủ đề B. Cả hai đều synchronisedtrên cùng một đối tượng và bên trong khối này có một biến toàn cục mà cả hai đang cập nhật;

static boolean commonVar = false;
Object lock = new Object;

...

void threadAMethod(){
    ...
    while(commonVar == false){
         synchornized(lock){
              ...
              commonVar = true
         }
    }
}

void threadBMethod(){
    ...
    while(commonVar == true){
         synchornized(lock){
              ...
              commonVar = false
         }
    }
}

Vì vậy, khi thread đi vào trong whilevòng lặp và giữ khóa, nó những gì nó đã làm và thiết lập commonVarđể true. Sau đó, sợi B đến, đi vào trong whilevòng lặp và vì commonVartruebây giờ, nó là có thể giữ khóa. Nó làm như vậy, thực thi synchronisedkhối và thiết lập commonVarlại false. Bây giờ, sợi A một lần nữa nhận được cửa sổ CPU mới của nó, nó về để thoát khỏi whilevòng lặp nhưng thread B vừa đặt nó trở lại false, do đó lặp đi lặp lại chu kỳ trên một lần nữa. Chủ đề làm một cái gì đó (vì vậy chúng không bị chặn theo nghĩa truyền thống) nhưng không có gì nhiều.

Có lẽ cũng rất hay khi đề cập đến việc livelock không nhất thiết phải xuất hiện ở đây. Tôi giả sử rằng trình lập lịch biểu ủng hộ luồng khác sau khi synchronisedkhối kết thúc thực thi. Hầu hết thời gian, tôi nghĩ rằng đó là một kỳ vọng khó đạt được và phụ thuộc vào nhiều điều xảy ra dưới mui xe.


Ví dụ tốt đẹp. Nó cũng minh họa tại sao bạn phải luôn luôn đọc và viết nguyên tử trong một bối cảnh đồng thời. Nếu các vòng lặp while nằm trong các khối đồng bộ hóa thì ở trên sẽ không thành vấn đề.
CognizantApe
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.