Як зробити мій ArrayList потокобезпечним? Інший підхід до проблеми на Java?


90

У мене є ArrayList, який я хочу використовувати для зберігання об’єктів RaceCar, які розширюють клас Thread, як тільки вони закінчаться. Клас, званий Race, обробляє цей ArrayList, використовуючи метод зворотного виклику, який викликає об'єкт RaceCar, коли його завершення виконується. Метод зворотного виклику, addFinisher (фінішер RaceCar), додає об’єкт RaceCar до ArrayList. Це має дати порядок, в якому потоки закінчують виконання.

Я знаю, що ArrayList не синхронізований і, отже, не є потокобезпечним. Я спробував використати метод Collections.synchronizedCollection (c Collection), передавши новий ArrayList і призначивши повернену Колекцію ArrayList. Однак це видає помилку компілятора:

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

Ось відповідний код:

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

Мені потрібно знати, чи використовую я правильний підхід, і якщо ні, то що я повинен використовувати, щоб зробити свій код безпечним для потоку? Дякую за допомогу!


2
(Зверніть увагу, Listінтерфейс насправді недостатньо повний, щоб бути дуже корисним у багатопотоковості.)
Том Хоутін - таклін

3
Я просто хотів би зазначити, що без цього Collections.synchronizedList()ми мали б тут РЕАЛЬНІ умови перегонів: P
Ділан Ватсон,

Перевірте це посилання programmerzdojo.com/java-tutorials/…
rishi007bansod

Відповіді:


147

Використовуйте Collections.synchronizedList().

Приклад:

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

2
Дякую! Я не впевнений, чому я не подумав просто використовувати Vector, бо пам’ятаю, десь читав, що вони були синхронізовані.
ericso

32
Можливо, це не гарна ідея працювати з класами, які визначено застарілими
frandevel

1
Хоча Vector є досить старим і не має підтримки колекцій, він не є застарілим. Ймовірно, краще використовувати Collections.synchronizedList (), як сказано іншими людьми.
Asturio

14
-1 для коментарів. Вектор не є застарілим, і як він не підтримує колекції? Він реалізує List. У javadoc для Vector конкретно сказано: "Починаючи з платформи Java 2 v1.2, цей клас був модернізований для реалізації інтерфейсу List, що робить його членом Java Collections Framework. На відміну від реалізації нової колекції, Vector синхронізується". Можуть бути вагомі причини для того, щоб не використовувати Vector (уникати синхронізації, змінювати реалізації), але те, що ви застаріли чи не сучасні, - не одна з них.
crazy4jesus

1
Використовуйте методи нижче: Collections.synchronizedList (список); Collections.synchronizedSet (set); Collections.synchronizedMap (карта); Вищевикладені методи приймають колекцію як параметр і повертають той самий тип колекції, які синхронізовані та безпечні для потоку.
Sameer Kazi

35

Зміна

private ArrayList finishingOrder;

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

до

private List finishingOrder;

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

Список - це надтип ArrayList, тому вам потрібно це вказати.

Інакше те, що ти робиш, здається добре. Інший варіант - ви можете використовувати вектор, який синхронізується, але це, мабуть, те, що я б зробив.


1
Або List, мабуть, було б корисніше. Або List<RaceCar>.
Tom Hawtin - таклін

Хороша думка, зробіть це приватним Список FinishingOrder = Collections.synchronizedList (...)
Преподобний Гонзо

Я спробував це, і компілятор зараз скаржиться на те, що я викликаю методи ArrayList у колекції: //Print out winner System.out.println("The Winner is " + ((RaceCar) finishingOrder.get(0)).toString() + "!"); він говорить, що метод get (0) не знайдений. Думки?
ericso

Вибачте за видалення та повторне додавання мого коментаря. Я намагався, щоб підсвічування працювало, використовуючи зворотні позначки. Я отримую OCD про такі речі.
ericso

Ні, це не працює. Він не перекине колекцію до списку: Race.java:41: знайдено несумісні типи: java.util.Collection потрібно: java.util.List finishOrder = Collections.synchronizedCollection (новий ArrayList (numberOfRaceCars));
ericso

11

CopyOnWriteArrayList

Використовуйте CopyOnWriteArrayListклас. Це поточно безпечна версія ArrayList.


3
Подумайте двічі, розглядаючи цей клас. Процитувавши документ класу: "Зазвичай це занадто дорого, але може бути ефективнішим, ніж альтернативи, коли операцій обходу значно більше, ніж мутацій, і корисно, коли ви не можете або не хочете синхронізувати обходи, однак потрібно виключити перешкоди між паралельними потоками . " Також див. Різницю між CopyOnWriteArrayList та synchronizedList
Василь Бурк

цей клас вступає в дію, коли ви рідко змінюєте список, але часто перебираєте елементи. наприклад, коли у вас є набір слухачів. ви реєструєте їх, а потім багато повторюєте ..., якщо вам явно не потрібен інтерфейс списку, але модифікуєте та читаєте операції, щоб бути одночасними, розгляньтеConcurrentLinkedQueue
benez

7

Можливо, ви використовуєте неправильний підхід. Те, що одна нитка, що імітує автомобіль, закінчується перед іншою ниткою симуляції автомобіля, не означає, що перша нитка повинна виграти змодельоване змагання.

Це багато в чому залежить від вашої програми, але, можливо, краще мати одну нитку, яка обчислює стан усіх автомобілів через невеликі проміжки часу, поки гонка не закінчиться. Або, якщо ви віддаєте перевагу використанню кількох ниток, ви можете попросити кожну машину записати "змодельований" час, необхідний для завершення гонки, і вибрати переможця як того, хто має найкоротший час.


Це хороший момент. Це лише вправа з тексту, який я використовую для вивчення Java. Суть полягала в тому, щоб навчитися використовувати потоки, і я фактично виходжу за рамки початкових специфікацій проблеми, будуючи механізм реєстрації переможців. Я хоч про використання таймера для вимірювання переможців. Але чесно кажучи, я думаю, що отримав те, що мені потрібно, від вправи.
ericso

5

Ви також можете використовувати synchronizedключове слово для такого addFinisherметоду

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

Таким чином, ви можете використовувати ArrayList add метод, безпечний для потоку, таким чином.


4
добре, але що, якщо у вас є два методи: addFinisher і delFinisher? Обидва методи безпечні для потоку, але оскільки обидва мають доступ до одного і того ж ArrayList, ви все одно отримаєте проблеми.
masi

1
@masi Тоді ви просто синхронізуєте на final Objectзамість кожного разу, коли ви отримуєте доступ до Collectionбудь-якого способу.
mkuech

2

Всякий раз, коли ви хочете використовувати безпечну версію об’єкта колекції мурашок, зверніться за допомогою до java.util.concurrent. * . Він має майже всі паралельні версії несинхронізованих об'єктів колекції. наприклад: для ArrayList у вас є java.util.concurrent.CopyOnWriteArrayList

Ви можете зробити Collections.synchronizedCollection (будь-який об’єкт колекції), але пам’ятайте про цю класичну синхронізацію. техніка дорога і поставляється з накладними витратами. Пакет java.util.concurrent. * є менш дорогим і краще керує продуктивністю за допомогою таких механізмів, як

копіювати на запис, порівнювати та міняти місцями, блокувати, ітератори знімків тощо.

Отже, віддайте перевагу чомусь із пакету java.util.concurrent. *


1

Ви можете замість цього використовувати як вектор, оскільки вектори безпечні для потоку, а список списків - ні. Хоча вектори старі, але вони можуть легко вирішити вашу мету.

Але ви можете зробити ваш Arraylist синхронізованим, як код, враховуючи це:

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

-1

Ви можете перейти з ArrayList на тип Vector, в якому кожен метод синхронізується.

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

5
Якщо ви збираєтеся запропонувати використовувати іншу колекцію, ймовірно, вектор - поганий вибір. Це застаріла колекція, яка була модернізована під дизайн нової платформи Java Collections Framework. Я впевнений, що в пакеті java.until.concurrent є кращий вибір.
Едвін Далорцо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.