Bagaimana cara membuat ArrayList Thread-Safe saya? Pendekatan lain untuk masalah di Jawa?

90

Saya memiliki ArrayList yang ingin saya gunakan untuk menampung objek RaceCar yang memperluas kelas Thread segera setelah selesai dijalankan. Sebuah kelas, yang disebut Race, menangani ArrayList ini menggunakan metode callback yang dipanggil oleh objek RaceCar saat selesai dijalankan. Metode callback, addFinisher (RaceCar finisher), menambahkan objek RaceCar ke ArrayList. Ini seharusnya memberi urutan di mana Threads selesai dieksekusi.

Saya tahu bahwa ArrayList tidak disinkronkan dan karenanya tidak aman untuk thread. Saya mencoba menggunakan metode Collections.synchronizedCollection (c Collection) dengan meneruskan ArrayList baru dan menetapkan Collection yang dikembalikan ke ArrayList. Namun, ini memberi saya kesalahan kompiler:

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

Berikut adalah kode yang relevan:

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);
    }

Yang perlu saya ketahui adalah, apakah saya menggunakan pendekatan yang benar dan jika tidak, apa yang harus saya gunakan untuk membuat kode saya aman untuk thread? Terima kasih untuk bantuannya!

ericso
sumber
2
(Catatan, Listantarmuka tidak cukup lengkap untuk menjadi sangat berguna dalam multithreading.)
Tom Hawtin - tackline
3
Saya hanya ingin menunjukkan bahwa, tanpa Collections.synchronizedList(), kami akan memiliki kondisi balapan NYATA di sini: P
Dylan Watson
Periksa tautan ini programmerzdojo.com/java-tutorials/…
rishi007bansod

Jawaban:

147

Gunakan Collections.synchronizedList().

Ex:

Collections.synchronizedList(new ArrayList<YourClassNameHere>())
Amir Afghani
sumber
2
Terima kasih! Saya tidak yakin mengapa saya tidak berpikir untuk hanya menggunakan Vektor karena saya ingat pernah membaca di suatu tempat mereka disinkronkan.
ericso
32
Mungkin bukan ide yang baik bekerja dengan kelas yang didefinisikan sebagai usang
frandevel
1
Meskipun Vector sudah cukup tua dan tidak memiliki dukungan Collections, Vector tidak digunakan lagi. Mungkin lebih baik menggunakan Collections.synchronizedList () seperti yang dikatakan orang lain di sini.
Asturio
14
-1 untuk komentar. Vektor tidak digunakan lagi dan bagaimana tidak memiliki dukungan koleksi? Ini mengimplementasikan Daftar. Javadoc untuk Vector secara khusus mengatakan: "Pada platform Java 2 v1.2, kelas ini telah dipasang untuk mengimplementasikan antarmuka List, menjadikannya anggota Java Collections Framework. Tidak seperti implementasi collection yang baru, Vector disinkronkan." Mungkin ada alasan bagus untuk tidak menggunakan Vector (menghindari sinkronisasi, mengubah implementasi), tetapi menjadi "usang" atau "tidak modern" bukanlah salah satunya.
Fool4jesus
1
Gunakan metode di bawah ini: Collections.synchronizedList (list); Collections.synchronizedSet (set); Collections.synchronizedMap (peta); Metode di atas mengambil koleksi sebagai parameter dan mengembalikan jenis koleksi yang sama yang disinkronkan dan aman untuk utas.
Sameer Kazi
35

Perubahan

private ArrayList finishingOrder;

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

untuk

private List finishingOrder;

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

List adalah supertipe ArrayList jadi Anda perlu menentukannya.

Jika tidak, apa yang Anda lakukan tampaknya baik-baik saja. Pilihan lainnya adalah Anda dapat menggunakan Vector, yang disinkronkan, tetapi ini mungkin yang akan saya lakukan.

Pendeta Gonzo
sumber
1
Atau Listmungkin akan lebih berguna. Atau List<RaceCar>.
Tom Hawtin - tackline
Poin bagus, jadikan pribadi List finishingOrder = Collections.synchronizedList (...)
Pendeta Gonzo
Saya mencoba ini dan kompilator sekarang mengeluh tentang saya memanggil metode ArrayList pada Koleksi: //Print out winner System.out.println("The Winner is " + ((RaceCar) finishingOrder.get(0)).toString() + "!"); Dikatakan bahwa metode get (0) tidak ditemukan. Pikiran?
ericso
Maaf tentang menghapus dan menambahkan kembali komentar saya. Saya mencoba untuk mendapatkan penyorotan untuk bekerja menggunakan backticks. Saya terkena OCD tentang hal-hal semacam itu.
ericso
Tidak, itu tidak berhasil. Ini tidak akan mentransmisikan Koleksi ke Daftar: Race.java:41: jenis yang tidak kompatibel ditemukan: java.util.Collection diperlukan: java.util.List finishingOrder = Collections.synchronizedCollection (new ArrayList (numberOfRaceCars));
ericso
11

CopyOnWriteArrayList

Gunakan CopyOnWriteArrayListkelas. Ini adalah versi aman utas ArrayList.

Singh Piyush
sumber
3
Pikirkan dua kali saat mempertimbangkan kelas ini. Mengutip dokumen kelas: "Ini biasanya terlalu mahal, tetapi mungkin lebih efisien daripada alternatif ketika operasi traversal jauh melebihi mutasi, dan berguna ketika Anda tidak dapat atau tidak ingin menyinkronkan traversal, namun perlu mencegah interferensi di antara thread yang bersamaan . ” Juga, lihat Perbedaan antara CopyOnWriteArrayList dan synchronizedList
Basil Bourque
kelas ini mulai berlaku ketika Anda jarang mengubah daftar, tetapi sering kali mengulang elemen. misalnya, jika Anda memiliki sekumpulan pendengar. Anda mendaftarkannya dan kemudian Anda sering mengulang ..., jika Anda tidak secara eksplisit memerlukan antarmuka daftar, tetapi memodifikasi dan membaca operasi agar bersamaan, pertimbangkanConcurrentLinkedQueue
benez
7

Anda mungkin menggunakan pendekatan yang salah. Hanya karena satu utas yang mensimulasikan mobil selesai sebelum utas simulasi mobil lainnya tidak berarti bahwa utas pertama harus memenangkan perlombaan yang disimulasikan.

Ini sangat bergantung pada aplikasi Anda, tetapi mungkin lebih baik memiliki satu utas yang menghitung keadaan semua mobil pada interval waktu kecil hingga balapan selesai. Atau, jika Anda lebih suka menggunakan beberapa utas, Anda mungkin meminta setiap mobil mencatat waktu "simulasi" yang dibutuhkan untuk menyelesaikan balapan, dan memilih pemenang sebagai pemenang dengan waktu tersingkat.

erickson
sumber
Itu poin yang bagus. Ini hanya latihan dari teks yang saya gunakan untuk belajar Java. Intinya adalah mempelajari cara menggunakan utas dan saya sebenarnya melampaui spesifikasi asli masalah dalam membangun mekanisme untuk mencatat pemenang. Saya berpikir tentang menggunakan pengatur waktu untuk mengukur pemenang. Tapi sejujurnya, saya pikir saya mendapatkan apa yang saya butuhkan dari latihan.
ericso
5

Anda juga dapat menggunakan synchronizedkata kunci untuk addFinishermetode seperti ini

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

Jadi Anda bisa menggunakan ArrayList menambahkan metode thread-safe dengan cara ini.

erhun
sumber
4
baik, tetapi bagaimana jika Anda mendapat dua metode: addFinisher dan delFinisher? Kedua metode ini aman untuk thread tetapi karena keduanya mengakses ArrayList yang sama, Anda masih akan mendapatkan masalah.
omni
1
@masi Kemudian Anda cukup menyinkronkan final Objectsetiap kali Anda mengaksesnya dengan Collectioncara apa pun.
mkuech
2

Kapan pun Anda ingin menggunakan versi aman ant thread dari objek ant collection, gunakan bantuan paket java.util.concurrent. * . Ini memiliki hampir semua versi objek koleksi yang tidak tersinkronisasi secara bersamaan. misalnya: untuk ArrayList, Anda memiliki java.util.concurrent.CopyOnWriteArrayList

Anda dapat melakukan Collections.synchronizedCollection (objek koleksi apa pun), tetapi ingat sinkronisasi klasik ini. teknik mahal dan dilengkapi dengan overhead kinerja. java.util.concurrent. * lebih murah dan mengelola kinerja dengan cara yang lebih baik dengan menggunakan mekanisme seperti

salin-saat-tulis, bandingkan-dan-tukar, Kunci, iterator snapshot, dll.

Jadi, Lebih suka sesuatu dari paket java.util.concurrent. *

Jaydeep Ramesh Deshmukh
sumber
1

Anda juga dapat menggunakan sebagai Vektor sebagai gantinya, karena vektor aman untuk utas dan daftar larik tidak. Meskipun vektor sudah tua tetapi mereka dapat menyelesaikan tujuan Anda dengan mudah.

Tetapi Anda dapat membuat Arraylist Anda disinkronkan seperti kode yang diberikan ini:

Collections.synchronizedList(new ArrayList(numberOfRaceCars())); 
Naman jain
sumber
-1

Anda dapat mengubah dari ArrayList ke tipe Vector, di mana setiap metode disinkronkan.

private Vector finishingOrder;
//Make a Vector to hold RaceCar objects to determine winners
finishingOrder = new Vector(numberOfRaceCars);
darlinton
sumber
5
Jika Anda akan menyarankan untuk menggunakan koleksi lain, mungkin Vector adalah pilihan yang buruk. Ini adalah koleksi warisan yang dipasang ke desain Java Collections Framework yang baru. Saya yakin ada pilihan yang lebih baik dalam paket java.until.concurrent.
Edwin Dalorzo