2013-09-24 25 views
5

Trong ứng dụng của chúng tôi, chúng tôi có ngoại lệ ArrayIndexOutOfBounds trên hoạt động ArrayList.add(Object o). Lời giải thích rõ ràng nhất là an toàn luồng, nhưng tôi không thể tạo lại các sự kiện. Tôi đã thử tạo hai chủ đề. Trong một tôi đang thêm các yếu tố, trong khác tôi loại bỏ chúng (hoặc thanh toán bù trừ mảng), nhưng tôi đã không nhận được ngoại lệ cho lần thứ hai. Tôi có nghĩa là nó rõ ràng nó có thể xảy ra bằng cách nhìn vào nguồn của ArrayList, nhưng nó sẽ là tốt đẹp để có thể chứng minh nó.Làm thế nào để chứng minh arraylist không phải là thread an toàn với một bài kiểm tra?

Tôi đã chạy thử nghiệm này trong một thời gian khá mà không cần bất kỳ ngoại lệ:

public class Test { 
static ArrayList a = new ArrayList(); 

public static void main(String[] args) throws Exception { 
    Thread t1 = new Thread() { 
     public void run() { 
      while (true) { 
       if (a.size() > 0) 
        a.remove(0); 
      } 
     } 
    }; 

    Thread t2 = new Thread() { 
     public void run() { 
      while (true) { 
       a.add(new Object()); 
      } 
     } 
    }; 

    t2.start(); 
    Thread.sleep(100); 
    t1.start(); 
} 
} 
+0

'ArrayList.add (Object)' sẽ không ném một 'ArrayIndexOutOfBoundsException'; 'ArrayList.add (index, Object)' sẽ. –

+0

guido: '' 'add (Object)' '' cũng có thể ném ngoại lệ (tôi đã nhìn thấy nó bằng chính mắt mình) nếu trạng thái bên trong của arraylist được thay đổi từ thread khác. Kiểm tra mã nguồn. – NeplatnyUdaj

+0

yep you are right: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ArrayList.java#ArrayList.add%28java.lang .Object% 29; nó phải được đồng bộ bên ngoài –

Trả lời

5

Nhờ nhận xét từ isnot2bad Tôi tìm thấy một vấn đề trong các giả định của tôi. Vấn đề là với việc thêm đồng thời, không thêm/xóa. tôi đã có thể tạo ra một thử nghiệm thất bại:

static ArrayList a = new ArrayList(1); 

public static void main(String[] args) throws Exception { 
    Thread t1 = new Thread() { 
     public void run() { 
      while (true) { 
       a.add(new Object()); 
      } 
     } 
    }; 

    Thread t2 = new Thread() { 
     public void run() { 
      while (true) { 
       a = new ArrayList(1); 
       a.add(new Object()); 
       a.add(new Object()); 
      } 
     } 
    }; 

    t2.start(); 
    Thread.sleep(100); 
    t1.start(); 
} 

Trên phù hợp với tiện ích tại các chủ đề đầu tiên, tôi nhận được điều này:

Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: 2 

:)

+1

Vì vậy, thay thế tốt hơn 'ArrayList' bằng một bộ sưu tập đồng thời như' ConcurrentLinkedQueue' hoặc đơn giản là 'Collections.synchronizedList (new ArrayList())' trong dự án của bạn! ;) – isnot2bad

+0

Trong trường hợp cụ thể này có. Tôi biết vấn đề ở đâu.Nhưng chưa bao giờ thấy điều đó trước đây. – NeplatnyUdaj

1

tôi có thể để tái tạo vấn đề của bạn chỉ đơn giản bằng cách thêm nhiều chủ đề của trình bổ sung.

0

Làm cho luồng người tiêu dùng ngủ ít hơn nhiều so với giấc ngủ của nhà sản xuất, ví dụ: 20 ms thay vì 100 ms. Bằng cách đó, cơ hội của ngoại lệ được ném là lớn hơn nhiều.

+0

Chủ đề không ngủ. Nó chỉ là một sự chậm trễ trước khi bắt đầu thread thứ hai. Dù sao mã đó không tạo ra hành vi mong muốn – NeplatnyUdaj

2

Thật khó để quan sát bất kỳ lỗi nào với mã đã cho bởi vì bạn không thực sự kiểm tra những gì được lưu trữ trong danh sách. Tôi không thể nói rằng nó không thể để có được một ArrayIndexOutOfBoundsException nhưng nó sẽ rất hiếm vì bạn chỉ có thể nhận được một khi mảng đang được thay đổi kích cỡ, và nó được thay đổi kích cỡ rất hiếm khi.

Nếu bạn kiểm tra đối tượng mà bạn xóa không trùng lặp, bạn có thể thấy hành vi không mong muốn hơn: bạn chỉ thêm đối tượng mới, vì vậy chuỗi xóa sẽ không bao giờ nhìn thấy cùng một đối tượng hai lần phải không? Không phải như vậy:

import java.util.*; 
public class Test { 
    static ArrayList a = new ArrayList(); 

    public static void main(String[] args) throws Exception { 
     Thread t1 = new Thread() { 
      public void run() { 
       Object x = null; 
       while (true) { 
        if (a.size() > 0) { 
         Object y = a.remove(0); 
         if (x == y) System.out.println("Duplicate!"); 
         x = y; 
        } 
       } 
      } 
     }; 

     Thread t2 = new Thread() { 
      public void run() { 
       while (true) { 
        a.add(new Object()); 
       } 
      } 
     }; 

     t2.start(); 
     Thread.sleep(100); 
     t1.start(); 
    } 
} 

Điều này xảy ra khi một đối tượng được bổ sung trong thời gian System.arrayCopy gọi: elementData[--size] = null bộ chỉ số mảng sai lầm khi nullsize không còn có giá trị nó đã có trong đầu của phương pháp.

Các vấn đề liên quan