2010-09-28 61 views
12

tôi đã tạo ArrayList đồng bộ nhưcách tạo đồng bộ ArrayList

import java.text.SimpleDateFormat; 
import java.util.*; 


class HelloThread 
{ 

int i=1; 
List arrayList; 
    public void go() 
    { 
arrayList=Collections.synchronizedList(new ArrayList()); 
Thread thread1=new Thread(new Runnable() { 

    public void run() { 
    while(i<=10) 
    { 
    arrayList.add(i); 
    i++; 
    } 
    } 
}); 
thread1.start(); 
Thread thred2=new Thread(new Runnable() { 
    public void run() { 
    while(true) 
    { 
    Iterator it=arrayList.iterator(); 
     while(it.hasNext()) 
     { 
     System.out.println(it.next()); 
     } 
    } 
    } 
}); 
thred2.start(); 
    } 
} 

public class test 
{ 
    public static void main(String[] args) 
    { 
    HelloThread hello=new HelloThread(); 
    hello.go(); 
    } 
} 

nhưng nhận được ngoại lệ này như

ngoại lệ này trong chủ đề "Chủ đề-1" java.util.ConcurrentModificationException

bất cứ điều gì sai trong cách tiếp cận của tôi?

Trả lời

23

Iterator của synchronizedList không (và không thể) đồng bộ, bạn cần phải đồng bộ hóa trên danh sách một cách thủ công trong khi iterating (xem javadoc):

synchronized(arrayList) { 
    Iterator it=arrayList.iterator(); 
    while(it.hasNext()) { 
     System.out.println(it.next()); 
    } 
} 

phương pháp khác là sử dụng một CopyOnWriteArrayList thay vì Collections.synchronizedList() . Nó thực hiện một ngữ nghĩa copy-on-write và do đó không yêu cầu đồng bộ hóa.

0

Bạn không thể sửa đổi Bộ sưu tập mà bạn đang lặp lại. Bạn có thể làm việc xung quanh điều này bằng cách truy cập các mục mảng theo chỉ mục, không thông qua một Iterator. Tôi có thể cung cấp thêm lời khuyên nếu bạn cho tôi biết vấn đề mà bạn đang cố gắng giải quyết với mã này.

+0

tôi có thể nhận được điểm của bạn tôi không thể sử dụng trình vòng lặp để truy cập arrayList đồng bộ. – Lalchand

2

Như Spike đã nói, bạn không thể sửa đổi bộ sưu tập trong khi lặp lại bộ sưu tập đó. Tuy nhiên, tôi nghĩ rằng giải pháp là khóa danh sách trong khi lặp lại.

class HelloThread 
{ 

int i=1; 
List arrayList; 
    public void go() 
    { 
arrayList=Collections.synchronizedList(new ArrayList()); 
Thread thread1=new Thread(new Runnable() { 

    public void run() { 
    while(i<=10) 
    { 
synchronized(someLock) { 
    arrayList.add(i); 
} 
    i++; 
    } 
    } 
}); 
thread1.start(); 
Thread thred2=new Thread(new Runnable() { 
    public void run() { 
    while(true) 
    { 
synchronized(someLock) { 
    Iterator it=arrayList.iterator(); 
     while(it.hasNext()) 
     { 
     System.out.println(it.next()); 
     } 
} 
    } 
    } 
}); 
thred2.start(); 
    } 
} 

public class test 
{ 
    public static void main(String[] args) 
    { 
    HelloThread hello=new HelloThread(); 
    hello.go(); 
    } 
} 

Tôi không chắc bạn đang cố gắng làm gì, vì vậy tôi hy vọng điều này sẽ không làm hỏng chức năng mã của bạn.

3

java.util.ConcurrentModificationException xảy ra khi bạn thao tác (thêm, xóa) một bộ sưu tập trong khi lặp qua cùng một bộ sưu tập.

Bạn có thể muốn tiêu thụ các mục tạo trong chuỗi thứ hai của mình trong khi sau khi chúng được tạo bởi chuỗi đầu tiên của bạn. Vì vậy, bạn có thể sử dụng ArrayLists get(index) và kích thước () để kiểm soát

9

Cân nhắc sử dụng CopyOnWriteArrayList an toàn chỉ. Mỗi khi bạn thêm một mục, một bản sao mới của mảng cơ bản được tạo ra. Tuy nhiên, trình lặp sẽ không phản ánh các bổ sung vào danh sách kể từ khi trình vòng lặp được tạo ra, nhưng được đảm bảo không được ném ConcurrentModificationException.

arrayList=new CopyOnWriteArrayList(); 
+1

+1 để không phát minh lại bánh xe. – BlairHippo

+1

Cần lưu ý rằng quá trình sao chép sao chép tham chiếu phần tử cho tất cả các phần tử danh sách. Điều này quy mô kém cho danh sách dài. –

10

câu trả lời khác đã xác định được vấn đề:

  • Các vòng lặp cho bộ sưu tập đồng bộ chưa đồng bộ. Trong thực tế, chúng đơn giản là các trình vòng lặp được trả về bởi các đối tượng thu thập bên trong các lớp trình bao bọc.

  • Nhiều lớp thu thập (bao gồm ArrayList) sử dụng cơ chế không nhanh để phát hiện các sửa đổi đồng thời trong khi lặp lại. Hành vi này được ghi rõ trong javadocs cho các lớp tương ứng. Đây là những gì bạn đang nhìn thấy.

Không phải tất cả các lớp thu thập đều làm điều này.Ví dụ, nhiều lớp sưu tập java.util.Concurrent... cho phép sửa đổi đồng thời trong khi lặp lại, nhưng thư giãn ngữ nghĩa của chuỗi lặp để kết quả sửa đổi có thể hoặc không thể hiển thị trong đối tượng được trình lặp lặp lại.

javadoc cho số Collections.synchronizedList() giải thích cách đồng bộ hóa trình lặp. Về cơ bản bạn làm điều này:

List list = Collections.synchronizedList(new ArrayList()); 
    ... 
synchronized (list) { 
    Iterator i = list.iterator(); // Must be in synchronized block 
    while (i.hasNext()) 
     foo(i.next()); 
} 

(Ngoài: bình thường nó không phải là an toàn để giả định rằng làm một cái gì đó như thế này sẽ làm việc Về lý thuyết, trong danh sách đồng bộ có thể sử dụng một đối tượng khóa riêng, và báo cáo kết quả synchronized sẽ không khóa. Tuy nhiên các javadocs nói rằng đây là những gì cần làm trong trường hợp này ... vì vậy nó là an toàn.)

Vấn đề với việc đó là khóa bộ sưu tập tạo ra một nút cổ chai tiềm năng đồng thời. Cách thay thế là sử dụng cấu trúc dữ liệu sao chép vào ghi nội bộ sao chép các phần liên quan của bộ sưu tập. Cách tiếp cận này có nghĩa là một trình lặp sẽ thấy một ảnh chụp nhanh của bộ sưu tập. Sửa đổi có thể được thực hiện cho bộ sưu tập đồng thời với một lần lặp, nhưng trình lặp không nhìn thấy chúng. Vấn đề với copy-on-write là những sửa đổi có khả năng đắt hơn rất nhiều.

Cuối cùng, bạn cần phải cân bằng các đặc tính và chi phí của các loại bộ sưu tập khác nhau thay đổi đồng thời wrt so với yêu cầu thực tế của bạn. Bạn có thể thoát khỏi trình vòng lặp không nhìn thấy tất cả các sửa đổi đồng thời không?

+0

Cảm ơn bạn rất nhiều. Bạn đã lưu ngày của tôi :) – Niklas

0

Hãy lấy một danh sách bình thường (được thực hiện bởi lớp ArrayList) và làm cho nó được đồng bộ hóa. Điều này được thể hiện trong lớp SynchronizedArrayList. Chúng ta chuyển phương thức Collections.synchronizedList một ArrayList mới của Strings. Phương thức này trả về một List of Strings đã đồng bộ. // Đây là lớp SynchronizedArrayList gói com.mnas.technology.automation.utility; nhập java.util.ArrayList; nhập java.util.Collections; nhập khẩu java.util.Iterator; nhập java.util.List; nhập org.apache.log4j.Logger; /** * * @author manoj.kumar * @email [email protected] * */ public class SynchronizedArrayList { tĩnh Logger log = Logger.getLogger (SynchronizedArrayList.class.getName()); chính tĩnh void chính (String [] args) {

List<String> synchronizedList = Collections.synchronizedList(new ArrayList<String>()); 
synchronizedList.add("Aditya"); 
synchronizedList.add("Siddharth"); 
synchronizedList.add("Manoj"); 

// when iterating over a synchronized list, we need to synchronize access to the synchronized list 
synchronized (synchronizedList) { 
Iterator<String> iterator = synchronizedList.iterator(); 
while (iterator.hasNext()) { 
log.info("Synchronized Array List Items: " + iterator.next()); 
} 
} 

} 
} 

Notice that when iterating over the list, this access is still done using a synchronized block that locks on the synchronizedList object. 
In general, iterating over a synchronized collection should be done in a synchronized block 
Các vấn đề liên quan