Sự khác biệt giữa StringBuilder và StringBuffer trong Java: Lựa chọn nào phù hợp cho bạn?

0 0 0

Người đăng: Gung Typical

Theo Viblo Asia

Khi làm việc với các chuỗi có thể thay đổi trong Java, bạn sẽ phải lựa chọn giữa StringBuilder và StringBuffer. Cả hai đều cho phép chỉnh sửa nội dung chuỗi, nhưng chúng khác nhau đáng kể về tính an toàn luồng, hiệu suất và ứng dụng. Hãy cùng phân tích đặc điểm của từng lớp và xem xét các ví dụ minh họa để biết khi nào nên sử dụng lớp nào.

Sự khác biệt chính giữa hai lớp này nằm ở việc StringBuffer được đồng bộ hóa, đảm bảo an toàn trong môi trường đa luồng, trong khi StringBuilder thì không. Điều này dẫn đến hiệu suất cao hơn của StringBuilder trong môi trường đơn luồng, nhưng lại tiềm ẩn rủi ro race condition nếu sử dụng trong môi trường đa luồng mà không có biện pháp bảo vệ bổ sung.

Chúng ta hãy cùng tìm hiểu chi tiết hơn về từng lớp.

1. StringBuilder: Lựa chọn hiệu quả cho môi trường luồng đơn

Do không có chi phí đồng bộ hóa, StringBuilder hoạt động nhanh hơn StringBuffer. Tuy nhiên, việc sử dụng StringBuilder trong môi trường đa luồng mà không có biện pháp an toàn bổ sung có thể dẫn đến race condition và các vấn đề đồng thời khác.

Một ví dụ điển hình là khi hai luồng cùng thêm ký tự vào StringBuilder, kết quả cuối cùng có thể có độ dài không như mong đợi. Điều này xảy ra do cả hai luồng cố gắng thêm ký tự đồng thời, dẫn đến việc ghi đè hoặc bỏ sót thao tác.

VD: Trình bày sự không an toàn của luồng trong StringBuilder

Trong ví dụ này, chúng ta sử dụng hai luồng để thêm ký tự vào một StringBuilder. Tuy nhiên, do thiếu đồng bộ hóa, chúng ta gặp phải tình trạng chạy đua :

public class StringBuilderBasics { public void threadUnsafe() { // Common resource being shared StringBuilder builder = new StringBuilder(); // Thread appending "A" 1000 times Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { builder.append("A"); } }); // Thread appending "B" 1000 times Thread t2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { builder.append("B"); } }); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } // Result: 1840 (unpredictable) System.out.println("Length: " + builder.toString().length()); } public static void main(String[] args) { new StringBuilderBasics().threadUnsafe(); }
}

Giải thích :

  • Do tính không an toàn của luồng, độ dài cuối cùng của StringBuilder đầu ra là không thể đoán trước (ví dụ: 1840 thay vì 2000 ).
  • Điều này xảy ra vì cả hai luồng đều cố gắng thêm các ký tự cùng lúc, dẫn đến ghi đè hoặc hủy bỏ các hoạt động .

2. StringBuffer: Tùy chọn an toàn cho môi trường đa luồng

StringBuffer là lựa chọn an toàn cho môi trường đa luồng, nơi tính nhất quán dữ liệu là rất quan trọng. Việc đồng bộ hóa đảm bảo rằng các thao tác trên chuỗi được thực hiện một cách an toàn, tránh xung đột giữa các luồng.

Tuy nhiên, điều này cũng đồng nghĩa với việc StringBuffer có hiệu suất thấp hơn StringBuilder do chi phí của việc đồng bộ hóa.

Ví dụ: An toàn luồng trong StringBuffer

Đây là ví dụ tương tự như trên, nhưng lần này sử dụng StringBuffer:

public class StringBufferBasics { public void threadSafe() { // Common resource being shared StringBuffer buffer = new StringBuffer(); // Thread appending "A" 1000 times Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { buffer.append("A"); } }); // Thread appending "B" 1000 times Thread t2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { buffer.append("B"); } }); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } // Result: 2000 System.out.println("Length: " + buffer.toString().length()); } public static void main(String[] args) { new StringBufferBasics().threadSafe(); }
}

Giải thích :

  • StringBuffer đảm bảo cả hai luồng đều được nối lại an toàn, đạt được độ dài mong đợi là 2000.
  • Trong khi chuỗi cuối cùng an toàn cho luồng, đầu ra có thể được xen kẽ (ví dụ: “AAABBB...” được trộn lẫn với nhau) vì thứ tự thực thi luồng không xác định.

Tóm lại, việc lựa chọn giữa StringBuilder và StringBuffer phụ thuộc vào ngữ cảnh cụ thể. Nếu bạn đang làm việc trong môi trường đơn luồng và hiệu suất là yếu tố quan trọng, hãy sử dụng StringBuilder.

Ngược lại, nếu ứng dụng của bạn là đa luồng và yêu cầu tính an toàn luồng, StringBuffer là lựa chọn phù hợp. Hiểu rõ sự đánh đổi giữa khả năng thay đổi, hiệu suất và tính an toàn luồng sẽ giúp bạn đưa ra quyết định tốt hơn khi làm việc với chuỗi trong Java.

Hy vọng các bạn nắm rõ sự khác biệt trong bài viết trên.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tổng hợp các bài hướng dẫn về Design Pattern - 23 mẫu cơ bản của GoF

Link bài viết gốc: https://gpcoder.com/4164-gioi-thieu-design-patterns/. Design Patterns là gì. Design Patterns không phải là ngôn ngữ cụ thể nào cả.

0 0 299

- vừa được xem lúc

Học Spring Boot bắt đầu từ đâu?

1. Giới thiệu Spring Boot. 1.1.

0 0 275

- vừa được xem lúc

Cần chuẩn bị gì để bắt đầu học Java

Cần chuẩn bị những gì để bắt đầu lập trình Java. 1.1. Cài JDK hay JRE.

0 0 50

- vừa được xem lúc

Sử dụng ModelMapper trong Spring Boot

Bài hôm nay sẽ là cách sử dụng thư viện ModelMapper để mapping qua lại giữa các object trong Spring nhé. Trang chủ của ModelMapper đây http://modelmapper.org/, đọc rất dễ hiểu dành cho các bạn muốn tìm hiểu sâu hơn. 1.

0 0 194

- vừa được xem lúc

[Java] 1 vài tip nhỏ khi sử dụng String hoặc Collection part 1

. Hello các bạn, hôm nay mình sẽ chia sẻ về mẹo check String null hay full space một cách tiện lợi. Mình sẽ sử dụng thư viện Lớp StringUtils download file jar để import vào thư viện tại (link).

0 0 71

- vừa được xem lúc

Deep Learning với Java - Tại sao không?

Muốn tìm hiểu về Machine Learning / Deep Learning nhưng với background là Java thì sẽ như thế nào và bắt đầu từ đâu? Để tìm được câu trả lời, hãy đọc bài viết này - có thể kỹ năng Java vốn có sẽ giúp bạn có những chuyến phiêu lưu thú vị. DJL là tên viết tắt của Deep Java Library - một thư viện mã ng

0 0 139