Synchronized trong Java được dùng để đồng bộ hóa các thread chạy đồng thời để đảm bảo tính đúng đắn của chương trình. Java cung cấp cơ chế đồng bộ hoá cho phép các lập trình viên có thể điều phối đường đi thread. Cơ chế này dựa trên khái niệm monitor và lock. Chúng ta có thể ví monitor như là một lá chắn bảo vệ các tài nguyên biến thuộc class, biến instance, etc), còn lock như một chìa khoá mà monitor dùng để ngăn chặn nhiều thread truy cập vào các tài nguyên mà monitor đang bảo vệ.
Khi một thread muốn truy cập vào các tài nguyên được bảo vệ vởi monitor chúng phải chiếm được lock để giành quyền truy cập, khi lock đã được chiếm bởi 1 thread, các thread khác sẽ được JVM sắp vào vùng chờ. Khi thread đã sử dụng xong các tài nguyên bên trong monitor nó sẽ tiến hành phát hành một lock mới, JVM sẽ xoá vùng chờ và cho phép các thread khác chiếm lock và truy cập vào monitor.
@Synchronized trong lombok
@Synchronized annotation là một cách sử dụng an toàn hơn của từ khóa Synchronized nhưng vẫn đảm bảo các tính năng hoạt động bình thường như khi sử dụng Synchronized một cách thủ công. Cũng giống như Synchronized thì annotation chỉ sử dụng được trên static và instance method.
Không giống như Synchronized, @Synchronized sẽ lock trên một biến private được đặt tên là $lock. Nếu biến $lock này chưa tồn tại thì nó lombok sẽ tự khởi tạo cho chúng ta. Còn nếu chúng ta chú thích annotation này trên static method thì nó sẽ lock trên một biến private static được đặt tên là $LOCK.
Nếu không muốn sử dụng 2 biến mặc định $lock và $LOCK thì chúng ta có thể truyền tham số là tên của object muốn thay thế và các chúng ta cũng phải khỏi tạo thủ công một biến có tên tương ứng.
Ví dụ chúng ta có đoạn code sử annotation của lombok như sau:
import lombok.Synchronized; public class SynchronizedExample { private final Object readLock = new Object(); @Synchronized public static void hello() { System.out.println("world"); } @Synchronized public int answerToLife() { return 42; } @Synchronized("readLock") public void foo() { System.out.println("bar"); } }
Nếu dịch ra đoạn code thông thường mà chúng ta phải viết sẽ là
public class SynchronizedExample { private static final Object $LOCK = new Object[0]; private final Object $lock = new Object[0]; private final Object readLock = new Object(); public static void hello() { synchronized($LOCK) { System.out.println("world"); } } public int answerToLife() { synchronized($lock) { return 42; } } public void foo() { synchronized(readLock) { System.out.println("bar"); } } }
Lưu ý khi sử dụng @Synchronized
Chúng ta để ý rằng 2 biến $lock và $LOCK được lombok generate ra là một mảng Object[] rỗng chứ không phải là new Object(). Lý do là bởi Object không thể serialize còn Object[] size-0 thì được. Vì thế việc sử dụng annotation này sẽ không ngăn cản object của chúng ta trong quá trình serialize.
Luôn thêm serialVersionUID vào class khi sử dụng @Synchronized ngừa trường hợp xóa annotation này đã được sử dụng trước đó. Vì khi xóa annotation này đồng nghĩa với việc 2 biến $lock và $LOCK cũng không còn, nếu không được cung cấp serialVersionUID mặc định thì giá trị này sẽ tự động thay đổi khiến chi việc serialize-deserialize của những object trước đây sẽ không còn khả thi.
Tóm lược
Qua đây chúng ta đã biết cách sử dụng @Synchronized trong lombok thay cho mệnh đề Synchronized. Ngoài việc sinh code tự động thì annotation này còn linh hoạt không làm ảnh hưởng đến quá trình serialize-deserialize của chúng ta.
Nguồn tham khảo