ArrayList를 스레드로부터 안전하게 만들려면 어떻게합니까? Java의 문제에 대한 또 다른 접근 방식?


90

실행이 완료되는 즉시 Thread 클래스를 확장하는 RaceCar 개체를 유지하는 데 사용하려는 ArrayList가 있습니다. Race라고하는 클래스는 RaceCar 객체가 실행이 끝나면 호출하는 콜백 메서드를 사용하여이 ArrayList를 처리합니다. 콜백 메서드 인 addFinisher (RaceCar finisher)는 RaceCar 객체를 ArrayList에 추가합니다. 이것은 스레드가 실행을 완료하는 순서를 제공합니다.

ArrayList가 동기화되지 않았으므로 스레드로부터 안전하지 않습니다. 새 ArrayList를 전달하고 반환 된 Collection을 ArrayList에 할당하여 Collections.synchronizedCollection (c Collection) 메서드를 사용해 보았습니다. 그러나 이것은 나에게 컴파일러 오류를 제공합니다.

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인터페이스는 멀티 스레딩에서 매우 유용 할만큼 충분히 완전하지 않습니다.)
Tom Hawtin-tackline

3
나는하지 않고, 단지 것을 지적하고 싶습니다 Collections.synchronizedList()P : 우리가 여기 진짜 경쟁 조건이 것,
딜런 왓슨

답변:


147

사용 Collections.synchronizedList().

전의:

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

2
감사! 동기화 된 어딘가를 읽은 것을 기억하기 때문에 Vector를 사용하지 않는 이유를 잘 모르겠습니다.
ericso

32
더 이상 사용되지 않는 것으로 정의 된 클래스로 작업하는 것은 좋은 생각이 아닐 수도 있습니다.
frandevel

1
Vector는 상당히 오래되었고 컬렉션 지원이 부족하지만 더 이상 사용되지 않습니다. 다른 사람들이 여기에서 말한 것처럼 Collections.synchronizedList ()를 사용하는 것이 좋습니다.
Asturio

14
-1 (댓글) Vector는 더 이상 사용되지 않으며 컬렉션이 어떻게 지원되지 않습니까? List를 구현합니다. Vector 용 javadoc은 구체적으로 다음과 같이 말합니다. "Java 2 플랫폼 v1.2에서이 클래스는 List 인터페이스를 구현하도록 개조되어 Java Collections Framework의 멤버가되었습니다. 새로운 컬렉션 구현과 달리 Vector는 동기화됩니다." Vector를 사용하지 않는 데는 타당한 이유가있을 수 있지만 (동기화 방지, 구현 변경) "구식"또는 "현대적이지 않음"은 그 중 하나가 아닙니다.
fool4jesus

1
아래 메서드를 사용하십시오. Collections.synchronizedList (list); Collections.synchronizedSet (set); Collections.synchronizedMap (map); 위의 메서드는 컬렉션을 매개 변수로 사용하고 동기화되고 스레드로부터 안전한 동일한 유형의 컬렉션을 반환합니다.
사미르 카지

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)

List는 ArrayList의 상위 유형이므로이를 지정해야합니다.

그렇지 않으면 당신이하고있는 일이 괜찮아 보입니다. 다른 옵션은 동기화 된 Vector를 사용할 수 있다는 것입니다.하지만 이것이 아마도 제가 할 일입니다.


1
아니면 List아마도 더 유용 할 것입니다. 또는 List<RaceCar>.
Tom Hawtin-tackline

좋은 지적입니다. 비공개 목록으로 만드세요. List finishOrder = Collections.synchronizedList (...)
Gonzo 목사님

나는 이것을 시도했고 컴파일러는 이제 Collection에서 ArrayList 메서드를 호출하는 것에 대해 불평 //Print out winner System.out.println("The Winner is " + ((RaceCar) finishingOrder.get(0)).toString() + "!"); 하고 있습니다. get (0) 메서드를 찾을 수 없다는 것입니다. 생각?
ericso

댓글을 삭제하고 다시 추가해서 죄송합니다. 백틱을 사용하여 강조 표시를 사용하려고했습니다. 나는 그런 종류의 것들에 대해 강박증을 얻습니다.
ericso

아니요, 작동하지 않습니다. 컬렉션을 목록으로 캐스트하지 않습니다. Race.java:41 : 호환되지 않는 유형 발견 : java.util.Collection 필수 : ​​java.util.List finishOrder = Collections.synchronizedCollection (new 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 ObjectCollection 어떤 방식 으로든에 액세스 할 때마다 대신 .
mkuech 2015

2

ant 컬렉션 객체의 ant thread safe 버전을 사용하려면 java.util.concurrent. * 패키지의 도움을 받으십시오 . 동기화되지 않은 컬렉션 개체의 거의 모든 동시 버전이 있습니다. 예 : ArrayList의 경우 java.util.concurrent.CopyOnWriteArrayList가 있습니다.

Collections.synchronizedCollection (모든 컬렉션 객체)을 수행 할 수 있지만이 고전적인 동기화를 기억하십시오. 기술은 비싸고 성능 오버 헤드가 있습니다. java.util.concurrent. * 패키지는 저렴하며 다음과 같은 메커니즘을 사용하여 더 나은 방식으로 성능을 관리합니다.

쓰기시 복사, 비교 및 ​​교체, 잠금, 스냅 샷 반복기 등

그래서, java.util.concurrent. * 패키지에서 무언가를 선호하십시오


1

벡터는 스레드로부터 안전하고 arraylist는 그렇지 않으므로 대신 Vector로 사용할 수도 있습니다. 벡터는 오래되었지만 목적을 쉽게 해결할 수 있습니다.

그러나 다음과 같은 코드처럼 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
다른 컬렉션 사용을 제안하려는 경우 아마도 Vector는 좋지 않은 선택입니다. 새로운 Java Collections Framework의 디자인에 맞게 개조 된 레거시 컬렉션입니다. java.until.concurrent 패키지에 더 나은 선택이 있다고 확신합니다.
Edwin Dalorzo 2014 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.