sách gpt4 ăn đã đi

Hãy nói về phân tích mã nguồn khóa CountDownLatch

In lại Tác giả: qq735679552 Thời gian cập nhật: 29-09-2022 22:32:09 30 4
mua khóa gpt4 giày nike

CFSDN nhấn mạnh vào giá trị tạo ra nguồn mở và chúng tôi cam kết xây dựng nền tảng chia sẻ tài nguyên để mọi nhân viên CNTT có thể tìm thấy thế giới tuyệt vời của bạn tại đây.

Bài viết trên blog CFSDN này nói về phân tích mã nguồn khóa CountDownLatch được tác giả sưu tầm và biên soạn. Nếu bạn quan tâm đến bài viết này thì nhớ like nhé.

Hãy nói về phân tích mã nguồn khóa CountDownLatch

Giới thiệu chức năng

Khóa là một lớp tiện ích đồng bộ hóa có chức năng trì hoãn tiến trình của một luồng cho đến khi nó đạt đến trạng thái kết thúc [CPJ 3.4.2]. Chức năng của khóa tương đương với một cánh cửa: trước khi khóa đạt trạng thái kết thúc, cửa luôn đóng và không có sợi chỉ nào có thể đi qua. Khi đạt đến trạng thái kết thúc, cửa sẽ mở và cho phép tất cả các sợi chỉ đi qua. Khi chốt về trạng thái cuối sẽ không còn thay đổi trạng thái nữa nên cửa sẽ mở mãi mãi. Khóa có thể được sử dụng để đảm bảo rằng một số hoạt động nhất định tiếp tục được thực hiện cho đến khi các hoạt động khác hoàn thành, ví dụ: .

  • Đảm bảo rằng quá trình tính toán không tiếp tục cho đến khi tất cả tài nguyên mà nó yêu cầu đã được khởi tạo. Khóa nhị phân (bao gồm hai trạng thái) có thể được sử dụng để biểu thị "tài nguyên R đã được khởi tạo" và tất cả các hoạt động yêu cầu R trước tiên phải đợi trên khóa này.
  • Đảm bảo rằng một dịch vụ được khởi động sau khi tất cả các dịch vụ khác mà nó phụ thuộc vào đã được khởi động. Mỗi dịch vụ có một khóa nhị phân liên quan. Khi khởi động dịch vụ S, trước tiên nó sẽ đợi các khóa của các dịch vụ khác mà S phụ thuộc vào. Sau khi tất cả các dịch vụ phụ thuộc được khởi động, khóa S sẽ được giải phóng để các dịch vụ khác phụ thuộc vào S có thể tiếp tục thực thi.
  • Đợi cho đến khi tất cả người tham gia thao tác (ví dụ: tất cả người chơi trong trò chơi nhiều người chơi) sẵn sàng trước khi tiếp tục. Trong trường hợp này, khóa sẽ đạt trạng thái kết thúc khi tất cả người chơi đã sẵn sàng.

Hãy nói về phân tích mã nguồn khóa CountDownLatch

Đếm ngược Latch.jpg 。

CountDownLatch là một triển khai khóa linh hoạt có thể được sử dụng trong nhiều tình huống khác nhau được đề cập ở trên. Nó có thể khiến một hoặc nhiều luồng chờ một tập hợp sự kiện xảy ra. Trạng thái chốt bao gồm một bộ đếm được khởi tạo thành số dương cho biết số lượng sự kiện cần chờ. Phương thức countDown giảm bộ đếm, cho biết rằng một sự kiện đã xảy ra, trong khi phương thức chờ đợi đợi bộ đếm về 0, điều này cho biết rằng tất cả các sự kiện cần chờ đã xảy ra. Nếu giá trị bộ đếm khác 0, thì chờ đợi sẽ chặn cho đến khi bộ đếm về 0 hoặc luồng chờ bị gián đoạn hoặc hết thời gian chờ.

Trường hợp sử dụng

Hai cách sử dụng chốt phổ biến được đưa ra trong TestHarness. TestHarness tạo một số lượng luồng nhất định và sử dụng chúng để thực hiện đồng thời các tác vụ được chỉ định. Nó sử dụng hai chốt, tương ứng là "Cổng bắt đầu" và "Cổng kết thúc". Giá trị ban đầu của bộ đếm cổng bắt đầu là 1 và giá trị ban đầu của bộ đếm cổng cuối là số lượng luồng công việc. Điều đầu tiên mỗi luồng công nhân phải làm là đợi ở cổng bắt đầu để đảm bảo rằng tất cả các luồng đã sẵn sàng trước khi bắt đầu thực thi. Điều cuối cùng mà mỗi luồng phải làm là giảm phương thức countDown gọi cổng cuối đi 1. Điều này cho phép luồng chính chờ một cách hiệu quả cho đến khi tất cả các luồng công việc hoàn thành thực thi, do đó có thể tính được thời gian tiêu thụ.

  1. công cộng lớp TestHarness { 
  2.  
  3.     công cộng thời gian dàiNhiệm vụ(số nguyên nThreads, tác vụ Runnable cuối cùng) ném InterruptedException { 
  4.         cuối cùng CountDownLatch startGate = new CountDownLatch(1); 
  5.         cuối cùng CountDownLatch endGate = new CountDownLatch(nThreads); 
  6.  
  7.          (số nguyên i = 0; i < nLuồng; i++) { 
  8.             Luồng t = luồng mới(() -> { 
  9.                 thử { 
  10.                     startGate.await(); 
  11.                     thử { 
  12.                         task.run(); 
  13.                     } Cuối cùng { 
  14.                         endGate.countDown(); 
  15.                     } 
  16.  
  17.                 } catch (Bỏ qua InterruptedException) { 
  18.  
  19.                 } 
  20.             }); 
  21.             t.bắt đầu(); 
  22.         } 
  23.  
  24.         bắt đầu dài = System.nanoTime(); 
  25.         startGate.countDown(); 
  26.         endGate.await(); 
  27.         dài kết thúc = System.nanoTime(); 
  28.         trở lại kết thúc - bắt đầu; 
  29.     } 
  30.  
  31.     công cộng tĩnh void main(String[] args) ném InterruptedException { 
  32.         TestHarness testHarness = new TestHarness(); 
  33.         AtomicInteger num = new AtomicInteger(0); 
  34.         dài thời gian = testHarness.timeTasks(10, () -> System.ngoài.println(số.incrementAndGet())); 
  35.         Hệ thống.ngoài.println("chi phí thời gian: " + thời gian + "bệnh đa xơ cứng"); 
  36.     } 
  37.  
  38. //Kết quả đầu ra 
  39. 10 
  40. trị giá thời gian: 2960900ms 

Tại sao nên sử dụng khóa trong TestHarness thay vì khởi động nó ngay sau khi luồng được tạo? Có lẽ chúng ta muốn kiểm tra thời gian cần thiết để n luồng thực thi đồng thời một tác vụ nhất định. Nếu bạn bắt đầu các chủ đề ngay sau khi chúng được tạo, thì chủ đề bắt đầu trước sẽ "dẫn trước" chủ đề bắt đầu sau và số lượng chủ đề đang hoạt động sẽ tăng hoặc giảm theo thời gian và mức độ cạnh tranh sẽ tiếp tục thay đổi. Cổng bắt đầu sẽ cho phép luồng chính giải phóng tất cả các luồng công việc trong thời gian thực, trong khi cổng kết thúc sẽ cho phép luồng chính chờ luồng cuối cùng hoàn thành thay vì đợi từng luồng hoàn thành tuần tự.

Tóm tắt cách sử dụng

CountDownLatch được sử dụng một lần. Giá trị của máy tính chỉ có thể được khởi tạo một lần trong hàm tạo. Không có cơ chế nào để đặt lại giá trị của nó sau khi sử dụng CountDownLatch.

Phân tích mã nguồn

Phân tích mã.

CountDownLatch vẫn được triển khai bằng cách sử dụng Tóm tắtQueuedSynchronizer ở cấp độ thấp nhất.

  1. CountDownLatch startGate = **mới **CountDownLatch(1); 

Trước tiên chúng ta hãy xem phương pháp xây dựng của nó và tạo một đối tượng đồng bộ.

  1. công cộng Đếm ngược chốt(số nguyên đếm) { 
  2.     nếu như (đếm < 0) ném ra ngoại lệ IllegalArgumentException mới("đếm < 0"); 
  3.     this.sync = Đồng bộ hóa mới(đếm); 

Đồng bộ hóa là một triển khai của Tóm tắtQueuedSynchronizer Từ nghĩa đen, chúng ta có thể đoán rằng nó được triển khai một cách công bằng.

  1. riêng tư tĩnh lớp cuối cùng Sync mở rộng AbstractQueuedSynchronizer { 
  2.     riêng tư tĩnh cuối cùng dài serialVersionUID = 4982264981922014374L; 
  3.  
  4.     //Người xây dựng 
  5.     Đồng bộ(số nguyên đếm) { 
  6.         setState(đếm); 
  7.     } 
  8.  
  9.     // Lấy số lượng tài nguyên 
  10.     số nguyên lấy Đếm() { 
  11.         trở lại lấy trạng thái(); 
  12.     } 
  13.  
  14.     // Lấy khóa 
  15.     được bảo vệ số nguyên thửAcquireShared(số nguyên có được) { 
  16.         trở lại (getState() == 0) ? 1 : -1; 
  17.     } 
  18.  
  19.     // nhả khóa 
  20.     boolean được bảo vệ tryReleaseShared(số nguyên phát hành) { 
  21.         // Giảm đếm; tín hiệu khi chuyển tiếp ĐẾN số không 
  22.          (;;) { 
  23.             số nguyên c = lấy trạng thái(); 
  24.             nếu (c == 0) 
  25.                 trở lại SAI
  26.             số nguyên tiếp theoc = c-1; 
  27.             // mở khóa CAS 
  28.             nếu (so sánhAndSetState(c, nextc)) 
  29.                 trở lại tiếp theo == 0; 
  30.         } 
  31.     } 

Nếu có một giá trị được tính toán trong phương thức chờ, thì luồng hiện tại sẽ vào hàng đợi AQS để tạo nút Node và luồng sẽ chuyển sang trạng thái chặn.

  1. công cộng void await() ném InterruptedException { 
  2.     sync.acquireSharedInterruptibly(1); 

Trên thực tế, điều quan trọng nhất là có được khóa chia sẻ.

  1. công cộng void cuối cùng acquireSharedInterruptibly(số nguyên tranh luận) 
  2.     ném InterruptedException { 
  3.     nếu (Thread.interrupted()) 
  4.         ném ngoại lệ InterruptedException mới(); 
  5.     nếu (tryAcquireShared(arg) < 0) 
  6.         doAcquireSharedInterruptibly(đối số); 

CountDownLatch.Sync triển khai phương thức tryAcquireShared Nếu getState() == 0, nó trả về 1, nếu không thì trả về -1. Nghĩa là, việc thực thi phương thức chờ sau khi tạo phiên bản CountDownLatch sẽ tiếp tục gọi doAcquireSharedInterruptibly(arg),

  1. // Liệu có thể lấy được khóa chia sẻ hay không 
  2. được bảo vệ số nguyên thửAcquireShared(số nguyên có được) { 
  3.     trở lại (getState() == 0) ? 1 : -1; 
  4.  
  5.  
  6. // Cố gắng lấy khóa hoặc tham gia hàng đợi 
  7. riêng tư void doAcquireSharedInterruptibly(số nguyên tranh luận) 
  8.     ném InterruptedException { 
  9.     Nút cuối cùng node = addWaiter(Node.SHARED); 
  10.     boolean không thành công = ĐÚNG VẬY
  11.     thử { 
  12.          (;;) { 
  13.             Nút cuối cùng p = node.predecessor(); 
  14.             nếu (p == đầu) { 
  15.                 số nguyên r = tryAcquireShared(đối số); 
  16.                 nếu (r >= 0) { 
  17.                     setHeadAndPropagate(nút, r); 
  18.                     P.Kế tiếp = vô giá trị; // trợ giúp GC 
  19.                     thất bại = SAI
  20.                     trở lại
  21.                 } 
  22.             } 
  23.             nếu (shouldParkAfterFailedAcquire(p, node) && 
  24.                 parkAndCheckInterrupt()) 
  25.                 ném ngoại lệ InterruptedException mới(); 
  26.         } 
  27.     } Cuối cùng { 
  28.         nếu (thất bại) 
  29.             hủy bỏAcquire(nút); 
  30.     } 

Nếu có các luồng đang chờ trong phương thức countDown, chúng sẽ được đánh thức hoặc số lượng tài nguyên CountDownLatch sẽ bị giảm.

  1. công cộng void countDown() { 
  2.     đồng bộ hóa. phát hànhChia sẻ(1); 

Mở khóa khóa chia sẻ thông qua ReleaseShared.

  1. công cộng boolean cuối cùng releaseShared(số nguyên đối số) { 
  2.     if (tryReleaseShared(arg)) { 
  3.         doReleaseShared(); 
  4.         trở lại ĐÚNG VẬY
  5.     } 
  6.     trở lại SAI

最终会调用 doReleaseShared 唤醒 AQS 中的头节点.

  1. private void doReleaseShared() { 
  2.     /* 
  3.          * Ensure that a release propagates, even if there are other 
  4.          * TRONG-progress acquires/releases.  This proceeds TRONG the usual 
  5.          * way của trying ĐẾN unparkSuccessor của head if it needs 
  6.          * signal. But if it does không, status  bộ ĐẾN PROPAGATE ĐẾN 
  7.          * ensure that upon release, propagation continues. 
  8.          * Additionally, we must loop TRONG trường hợp a new node  added 
  9.          * while we are doing this. Also, unlike other uses của 
  10.          * unparkSuccessor, we need ĐẾN know if CAS ĐẾN reset status 
  11.          * fails, if so rechecking. 
  12.          */ 
  13.      (;;) { 
  14.         Node h = head; 
  15.         if (h != vô giá trị && h != tail) { 
  16.             số nguyên ws = h.waitStatus; 
  17.             if (ws == Node.SIGNAL) { 
  18.                 if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) 
  19.                     Tiếp tục;            // loop ĐẾN recheck cases 
  20.                 unparkSuccessor(h); 
  21.             } 
  22.             khác if (ws == 0 && 
  23.                      !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) 
  24.                 Tiếp tục;                // loop TRÊN failed CAS 
  25.         } 
  26.         if (h == head)                   // loop if head changed 
  27.             phá vỡ; 
  28.     } 

详细流程如下图:

源码流程图

Hãy nói về phân tích mã nguồn khóa CountDownLatch

CountDownLatch 闭锁源码分析.png 。

参考资料 。

《Java 并发编程实战》 。

https://www.cnblogs.com/Lee_xy_z/p/10470181.html 。

原文链接:https://mp.weixin.qq.com/s/7rn6NCPqIcGiDs3cVuVm2g 。

最后此篇关于聊聊 CountDownLatch 闭锁源码分析的文章就讲到这里了,如果你想了解更多关于聊聊 CountDownLatch 闭锁源码分析的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

30 4 0
qq735679552
Hồ sơ

Tôi là một lập trình viên xuất sắc, rất giỏi!

Nhận phiếu giảm giá taxi Didi miễn phí
Phiếu giảm giá taxi Didi
Chứng chỉ ICP Bắc Kinh số 000000
Hợp tác quảng cáo: 1813099741@qq.com 6ren.com
Xem sitemap của VNExpress