Nói chung nó là một ý tưởng rất xấu đến hoạt động đồng thời trên một datastructure không được thread-safe . Bạn không đảm bảo rằng việc triển khai sẽ không thay đổi trong tương lai, điều này có thể ảnh hưởng nghiêm trọng đến hành vi thời gian chạy của ứng dụng, tức là java.util.HashMap gây ra các vòng lặp vô hạn khi được đồng thời sửa đổi.
Để truy cập Danh sách đồng thời, Java cung cấp java.util.concurrent.CopyOnWriteArrayList
.Sử dụng thực hiện này sẽ giải quyết vấn đề của bạn theo những cách khác nhau:
- nó được đề an toàn, cho phép sửa đổi đồng thời
- iterating trên ảnh chụp nhanh của danh sách không bị ảnh hưởng bởi các hoạt động bổ sung đồng thời, cho phép đồng thời bổ sung và lặp
- đó là nhanh hơn so với đồng bộ hóa
Ngoài ra, nếu không sử dụng một bản sao của nội mảng là một yêu cầu nghiêm ngặt (mà tôi không thể tưởng tượng trong trường hợp của bạn, mảng này khá nhỏ vì nó chỉ chứa tham chiếu đối tượng, có thể được sao chép trong bộ nhớ khá hiệu quả), bạn có thể đồng bộ hóa quyền truy cập trên bản đồ. Nhưng điều đó sẽ yêu cầu Bản đồ được khởi tạo đúng cách, nếu không mã của bạn có thể ném NullPointerException
vì thứ tự thực thi chuỗi không được đảm bảo (bạn giả sử rằng populateList()
được bắt đầu trước đó, do đó danh sách sẽ được khởi tạo Khi sử dụng khối được đồng bộ hóa Trong trường hợp bạn có toàn bộ nội dung của phương thức run()
trong khối đồng bộ, chuỗi trình đọc phải chờ cho đến khi kết quả từ con trỏ được xử lý - có thể mất một thời gian - vì vậy bạn thực sự mất tất cả đồng thời
Nếu bạn quyết định đi đến khối được đồng bộ hóa, tôi sẽ thực hiện các thay đổi sau (và tôi không tuyên bố, chúng hoàn toàn chính xác):
Khởi tạo các trường danh sách vì vậy chúng tôi có thể đồng bộ hóa truy cập vào nó:
private List<String> mList = new ArrayList<>(); //initialize the field
Đồng bộ hóa các hoạt động sửa đổi (thêm). Không đọc dữ liệu từ con trỏ bên trong khối đồng bộ hóa vì nếu hoạt động của nó có độ trễ thấp, không thể đọc mList trong quá trình hoạt động đó, chặn tất cả các luồng khác trong một thời gian.
//mList = new ArrayList<>(); remove that line in your code
String data = cursor.getString(cursor.getColumnIndex(DataProvider.NAME)); //do this before synchronized block!
synchronized(mList){
mList.add(data);
}
Các lặp đọc phải bên trong khối đồng bộ, vì vậy không có yếu tố được thêm vào, trong khi được lặp lại:
synchronized(mList){
for (String name : mList) {
if (name.equals(query) {
return true;
}
}
}
Vì vậy, khi hai luồng hoạt động trên danh sách, một thread có thể thêm một một phần tử hoặc lặp lại toàn bộ danh sách tại một thời điểm. Bạn không thực thi song song trên các phần này của mã.
Về phiên bản được đồng bộ hóa của Danh sách (ví dụ: Vector
, Collections.synchronizedList()
). Những điều đó có thể kém hiệu quả hơn vì đồng bộ hóa bạn thực sự mất thực thi song song vì chỉ có một luồng có thể chạy các khối được bảo vệ tại một thời điểm. Hơn nữa, chúng vẫn có thể dễ bị ConcurrentModificationException
, thậm chí có thể xảy ra trong một chuỗi. Nó được ném, nếu datastructure được sửa đổi giữa iterator creation và iterator nên tiến hành. Vì vậy, những cơ sở dữ liệu đó sẽ không giải quyết được vấn đề của bạn.
Tôi cũng không khuyến nghị sử dụng đồng bộ thủ công, vì nguy cơ làm sai chỉ là quá cao (đồng bộ hóa sai hoặc khác nhau, khối đồng bộ hóa quá lớn, ...)
TL; DR
sử dụng một java.util.concurrent.CopyOnWriteArrayList
Tôi thích 'CopyOnWriteArrayList' hơn, nhưng OP cho biết" không sao chép ". Có thể anh ta muốn đảm bảo "chỉ một luồng có thể chạy các khối được bảo vệ cùng một lúc". – beatngu13
"không sao chép" -requirement là loại vô nghĩa. Chắc chắn, các CopyOnWriteArrayList * không * sao chép nội dung, nhưng đó là một phần của các chi tiết thực hiện. Cách thay thế duy nhất là có một khối đồng bộ, đồng bộ hóa trên chính bản đồ, dễ bị lỗi khi nó được khởi tạo bằng 'null'. Sử dụng 'Vector' không ngăn cản' ConcurrentModificationException', vì nó không có gì để làm với concurrency (nó được ném khi một iterator cố gắng tiếp tục nhưng danh sách đã được sửa đổi sau khi tạo trình lặp) –
Cảm ơn bạn đã giải trình. Tôi đoán 'CopyOnWriteArrayList' là tốt hơn cho trường hợp của tôi (kể từ khi tôi truy cập vào trường trên thread UI). – Nikolai