Làm cách nào để tạo An toàn cho Chuỗi ArrayList của tôi? Một cách tiếp cận khác cho vấn đề trong Java?


90

Tôi có một ArrayList mà tôi muốn sử dụng để giữ các đối tượng RaceCar mở rộng lớp Thread ngay sau khi chúng hoàn tất thực thi. Một lớp, được gọi là Race, xử lý ArrayList này bằng cách sử dụng một phương thức gọi lại mà đối tượng RaceCar gọi khi nó được thực thi xong. Phương thức gọi lại, addFinisher (Trình kết thúc RaceCar), thêm đối tượng RaceCar vào ArrayList. Điều này được cho là để đưa ra thứ tự mà các Luồng kết thúc thực thi.

Tôi biết rằng ArrayList không được đồng bộ hóa và do đó không an toàn cho chuỗi. Tôi đã thử sử dụng phương thức Collections.synchronizedCollection (c Collection) bằng cách chuyển vào ArrayList mới và gán Bộ sưu tập được trả về cho ArrayList. Tuy nhiên, điều này khiến tôi gặp lỗi trình biên dịch:

Race.java:41: incompatible types
found   : java.util.Collection
required: java.util.ArrayList
finishingOrder = Collections.synchronizedCollection(new ArrayList(numberOfRaceCars));

Đây là mã liên quan:

public class Race implements RaceListener {
    private Thread[] racers;
    private ArrayList finishingOrder;

    //Make an ArrayList to hold RaceCar objects to determine winners
    finishingOrder = Collections.synchronizedCollection(new ArrayList(numberOfRaceCars));

    //Fill array with RaceCar objects
    for(int i=0; i<numberOfRaceCars; i++) {
    racers[i] = new RaceCar(laps, inputs[i]);

        //Add this as a RaceListener to each RaceCar
        ((RaceCar) racers[i]).addRaceListener(this);
    }

    //Implement the one method in the RaceListener interface
    public void addFinisher(RaceCar finisher) {
        finishingOrder.add(finisher);
    }

Những gì tôi cần biết là, tôi có đang sử dụng cách tiếp cận đúng hay không và nếu không, tôi nên sử dụng cách nào để làm cho chuỗi mã của mình an toàn? Cảm ơn đã giúp đỡ!


2
(Lưu ý, các Listgiao diện không phải là thực sự hoàn toàn đủ để thể rất hữu ích trong việc đa luồng.)
Tom Hawtin - tackline

3
Tôi chỉ muốn đến thời điểm ra rằng, không Collections.synchronizedList(), chúng tôi muốn có một điều kiện REAL đua ở đây: P
Dylan Watson

Câu trả lời:


145

Sử dụng Collections.synchronizedList().

Ví dụ:

Collections.synchronizedList(new ArrayList<YourClassNameHere>())

2
Cảm ơn! Tôi không chắc tại sao tôi không nghĩ chỉ sử dụng một Vector vì tôi nhớ đã đọc ở đâu đó chúng được đồng bộ hóa.
ericso

32
Có lẽ nó không phải là một ý tưởng tốt làm việc với các lớp học được xác định là bị phản đối
frandevel

1
Mặc dù Vector đã khá cũ và thiếu hỗ trợ Collections, nhưng nó không bị phản đối. Có lẽ tốt hơn là sử dụng Collections.synchronizedList () như những người khác đã nói ở đây.
Asturio

14
-1 cho ý kiến. Vector không bị phản đối và làm cách nào để nó không hỗ trợ các bộ sưu tập? Nó thực hiện Danh sách. Đặc biệt javadoc dành cho Vector cho biết: "Kể từ nền tảng Java 2 v1.2, lớp này đã được trang bị thêm để triển khai giao diện Danh sách, khiến nó trở thành một thành viên của Khung tập hợp Java. Không giống như các triển khai tập hợp mới, Vector được đồng bộ hóa." Có thể có những lý do chính đáng để không sử dụng Vector (tránh đồng bộ hóa, thay đổi triển khai), nhưng "lỗi thời" hoặc "không hiện đại" không phải là một trong số đó.
fool4jesus

1
Sử dụng các phương pháp dưới đây: Collections.synchronizedList (list); Collections.synchronizedSet (bộ); Collections.synchronizedMap (bản đồ); Các phương thức trên lấy bộ sưu tập làm tham số và trả về cùng một loại bộ sưu tập được đồng bộ hóa và an toàn cho luồng.
Sameer Kazi

35

Thay đổi

private ArrayList finishingOrder;

//Make an ArrayList to hold RaceCar objects to determine winners
finishingOrder = Collections.synchronizedCollection(new ArrayList(numberOfRaceCars)

đến

private List finishingOrder;

//Make an ArrayList to hold RaceCar objects to determine winners
finishingOrder = Collections.synchronizedList(new ArrayList(numberOfRaceCars)

Danh sách là một siêu kiểu của ArrayList vì vậy bạn cần chỉ định điều đó.

Nếu không, những gì bạn đang làm có vẻ ổn. Tùy chọn khác là bạn có thể sử dụng Vector, được đồng bộ hóa, nhưng đây có thể là những gì tôi sẽ làm.


1
Hoặc Listcó lẽ sẽ hữu ích hơn. Hoặc List<RaceCar>.
Tom Hawtin - tackline

Tốt, hãy đặt nó ở chế độ riêng tư Kết thúc danh sáchOrder = Collections.synchronizedList (...)
Reverend Gonzo

Tôi đã thử điều này và trình biên dịch hiện đang phàn nàn về việc tôi gọi các phương thức ArrayList trên Bộ sưu tập: //Print out winner System.out.println("The Winner is " + ((RaceCar) finishingOrder.get(0)).toString() + "!"); Nó nói rằng phương thức get (0) không được tìm thấy. Suy nghĩ?
ericso

Xin lỗi về việc xóa và thêm lại bình luận của tôi. Tôi đã cố gắng làm cho phần highlight hoạt động bằng cách sử dụng backticks. Tôi nhận được OCD về loại công cụ đó.
ericso

Không, điều đó không hiệu quả. Nó sẽ không truyền Bộ sưu tập thành Danh sách: Race.java:41: tìm thấy các loại không tương thích: java.util.Collection yêu cầu: java.util.List finishOrder = Collections.synchronizedCollection (new ArrayList (numberOfRaceCars));
ericso

11

CopyOnWriteArrayList

Sử dụng CopyOnWriteArrayListlớp học. Đây là phiên bản an toàn chủ đề của ArrayList.


3
Hãy suy nghĩ kỹ khi xem xét lớp học này. Để trích dẫn tài liệu lớp: "Điều này thường quá tốn kém, nhưng có thể hiệu quả hơn các lựa chọn thay thế khi các hoạt động truyền tải vượt quá nhiều đột biến và hữu ích khi bạn không thể hoặc không muốn đồng bộ hóa các chuyển tải, nhưng cần phải loại trừ sự can thiệp giữa các luồng đồng thời . ” Ngoài ra, xem sự khác biệt giữa CopyOnWriteArrayList và synchronizedList
Basil Bourque

lớp này phát huy tác dụng khi bạn hiếm khi sửa đổi danh sách, nhưng thường lặp lại qua các phần tử. ví dụ: khi bạn có một nhóm người nghe. bạn đăng ký họ và sau đó bạn lặp rất nhiều ..., nếu bạn không cần phải rõ ràng giao diện danh sách, nhưng chỉnh sửa và đọc các hoạt động được đồng thời, xem xétConcurrentLinkedQueue
benez

7

Bạn có thể đang sử dụng cách tiếp cận sai. Chỉ vì một chuỗi mô phỏng một chiếc ô tô kết thúc trước một chuỗi mô phỏng ô tô khác không có nghĩa là chuỗi đầu tiên sẽ chiến thắng trong cuộc đua mô phỏng.

Nó phụ thuộc rất nhiều vào ứng dụng của bạn, nhưng có thể tốt hơn nếu có một luồng tính toán trạng thái của tất cả các xe trong những khoảng thời gian nhỏ cho đến khi cuộc đua hoàn tất. Hoặc, nếu bạn muốn sử dụng nhiều chuỗi, bạn có thể yêu cầu mỗi chiếc xe ghi lại thời gian "mô phỏng" để hoàn thành cuộc đua và chọn người chiến thắng là chiếc có thời gian ngắn nhất.


Đó là một điểm hay. Đây chỉ là một bài tập từ một văn bản tôi đang sử dụng để học Java. Mục đích là tìm hiểu cách sử dụng các chuỗi và tôi thực sự đang vượt ra ngoài các thông số kỹ thuật ban đầu của vấn đề trong việc xây dựng cơ chế ghi lại những người chiến thắng. Tôi nghĩ về việc sử dụng bộ đếm thời gian để đo những người chiến thắng. Nhưng thành thật mà nói, tôi nghĩ rằng tôi đã có được những gì tôi cần từ bài tập.
ericso

5

Bạn cũng có thể sử dụng synchronizedtừ khóa cho addFinisherphương pháp như thế này

    //Implement the one method in the RaceListener interface
    public synchronized void addFinisher(RaceCar finisher) {
        finishingOrder.add(finisher);
    }

Vì vậy, bạn có thể sử dụng ArrayList thêm phương thức luồng an toàn với cách này.


4
tốt, nhưng nếu bạn có hai phương thức: addFinisher và delFinisher thì sao? Cả hai phương pháp đều an toàn theo luồng nhưng vì cả hai đều truy cập vào cùng một ArrayList nên bạn sẽ vẫn gặp sự cố.
masi

1
@masi Sau đó, bạn chỉ cần đồng bộ hóa trên một final Objectthay thế mỗi khi bạn truy cập Collectiontheo bất kỳ cách nào.
mkuech

2

Bất cứ khi nào bạn muốn sử dụng phiên bản ant thread an toàn của đối tượng ant collection, hãy sử dụng gói java.util.concurrent. * . Nó có gần như tất cả các phiên bản đồng thời của các đối tượng thu thập không đồng bộ. ví dụ: đối với ArrayList, bạn có java.util.concurrent.CopyOnWriteArrayList

Bạn có thể thực hiện Collections.synchronizedCollection (bất kỳ đối tượng bộ sưu tập nào), nhưng hãy nhớ đồng bộ cổ điển này. kỹ thuật đắt tiền và đi kèm với chi phí lâu dài. gói java.util.concurrent. * ít tốn kém hơn và quản lý hiệu suất theo cách tốt hơn bằng cách sử dụng các cơ chế như

copy-on-write, so sánh và hoán đổi, Khóa, trình lặp ảnh chụp nhanh, v.v.

Vì vậy, hãy thích thứ gì đó từ gói java.util.concurrent. *


1

Thay vào đó, bạn cũng có thể sử dụng dưới dạng Vectơ, vì vectơ là chuỗi an toàn và danh sách mảng thì không. Mặc dù vectơ đã cũ nhưng chúng có thể giải quyết mục đích của bạn một cách dễ dàng.

Nhưng bạn có thể làm cho Danh sách mảng của mình được đồng bộ hóa như mã được cung cấp sau:

Collections.synchronizedList(new ArrayList(numberOfRaceCars())); 

-1

Bạn có thể thay đổi từ kiểu ArrayList sang kiểu Vector, trong đó mọi phương thức đều được đồng bộ hóa.

private Vector finishingOrder;
//Make a Vector to hold RaceCar objects to determine winners
finishingOrder = new Vector(numberOfRaceCars);

5
Nếu bạn định đề xuất sử dụng một bộ sưu tập khác, có lẽ Vector là một lựa chọn không tồi. Đây là một bộ sưu tập kế thừa được trang bị thêm cho thiết kế của Khung Bộ sưu tập Java mới. Tôi chắc chắn rằng có nhiều lựa chọn tốt hơn trong gói java.until.concurrent.
Edwin Dalorzo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.