Synchronization trong Java

Chào mừng các bạn đã trở lại với thachleblog. Ở bài trước, mình đã đề cập đến vấn đề thread interference khi sử dụng multi thread trong Java. Một trong những giải pháp cho vấn đề này chính là sử dụng synchronization, vậy sử dụng synchronization như thế nào, synchronization hoạt động như thế nào, chúng ta cùng bắt đầu nhé.

Ví dụ sử dụng synchronization để giải quyết thread interference ở ví dụ trước:

  1. package thach.le.synchroniztion;
  2. public class Synchronization {
  3. public static void main(String[] args) throws InterruptedException {
  4. Account counter = new Account();
  5. Runnable runnable = () -> {
  6. counter.increment(1);
  7. };
  8. Thread[] threads = new Thread[1000];
  9. for (int i = 0; i < threads.length; i++) {
  10. threads[i] = new Thread(runnable);
  11. threads[i].start();
  12. }
  13. // wait for all thread are finished
  14. for (int i = 0; i < threads.length; i++) {
  15. threads[i].join();
  16. }
  17. System.out.println("Count: " + counter.value());
  18. }
  19. }
  20.  
  21. class Account {
  22. private Object key = new Object();
  23. private int amount = 0;
  24. public void increment(int num) {
  25. synchronized (key) {
  26. amount = amount + num;
  27. }
  28. }
  29. public synchronized int value() {
  30. return amount;
  31. }
  32. }

Kết quả sẽ luôn là 1000
synchronization trong Java

Synchronization chính là khả năng đảm bảo đoạn code được thực thi bởi duy nhất một thread ở cùng 1 thời điểm.

Ta có thể sử dụng sychronization bằng các cách:

  • Synchronized method: đặt từ khóa synchronized trong method
  • Synchronized block: đặt từ khóa synchronized bên trong method

Synchronization hoạt động như thế nào?

Ở ví dụ trên mình dùng synchronized block, synchronized block sử dụng 1 object dùng để “lock” đoạn code bên trong method  increment() (mình sẽ gọi object này là lock object).

Object này chứa 1 value gọi là key (trong Java tất cả các object đều có key để sử dụng cho synchronization). Khi một thread thực thi đoạn synchronization code, thread đó sẽ chiếm giữ key của lock object, synchronization đảm bảo rằng, các thread chỉ thực thi đoạn code khi key của lock object là available (không có thread nào sử dụng). Do đó, tại 1 thời điểm, chỉ có 1 thread thực thi đoạn synchroniztion code.

Tiến trình đó được mô tả như sau:

  • Lock object có duy nhất 1 key, key này sẽ có trạng thái là available khi không có thread nào sử dụng
  • Khi một thread thực thi đoạn synchronization code, thread đó sẽ nắm giữ key của lock object, key sẽ có trạng thái là in – use
  • Khi một thread thực thi xong đoạn synchronization code, key của lock object trở về trạng thái available cho các thread khác sử dụng

Sự khác nhau giữa synchronized method và synchronized block

Có lẽ đã quá rõ ràng nhưng có lẽ mình cũng cần nói thêm một chút. Dùng synchronized method có nghĩa là cả method sẽ bị block bởi 1 thread khi thread đó thực thi. Cách này đơn giản nhưng đôi lúc hơi cưng nhắc. Do đó, nên ưu tiên dùng synchronized block để giảm tối đa đoạn code bị lock, giảm đến mức thấp nhất tình trạng “thắt nút cổ chai” – tất cả các thread phải “đứng đợi” ngoài method.

Vì đặc điểm đảm bảo chỉ duy nhất một thead được thao tác với dữ liệu và thao tác kiểm tra lock object trong method nên synchronization sẽ ảnh hưởng đến performance của ứng dụng. Do đó, chỉ sử dụng synchronization trong trường lợp share data giữa các thread.

Vừa rồi là phần trình bày của mình về synchronization, sử dụng synchronization sẽ ngăn chặn được khả năng xảy ra thread interference, nhưng sẽ có khả năng gây ra vấn đề khác, vấn đề đó là gì? mình sẽ trình bày ở bài sau, các bạn nhớ theo dõi nhé. Rất mong nhận được ý kiến đóng góp của tất cả các bạn.

Xin cảm ơn.
Tham khảo thêm:
https://app.pluralsight.com/player?course=java-patterns-concurrency-multi-threading
https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html

2 thoughts on “Synchronization trong Java

  1. Pingback: Deadlock trong Java – thachleblog | Java Technologies

  2. Pingback: Deadlock trong Java » Thach Le

Tham gia bình luận