2017-10-20 18 views
0

분의 시간에 나는 방금 대다수의 메소드에 동기화를 추가했습니다. 스레드 안전성을 보장하기 위해 구현해야 할 것이 있습니까?순환 대기열을 완전히 스레드 안전성으로 만드는 방법

또한이 문제를 해결할 수있는 더 좋은 방법이 있습니까? 분당 한 스레드 만 순환 큐를 한 번에 사용할 수 있습니다. 이는 약간 비효율적 인 것처럼 보입니다.

class CircularQueue<T> implements Iterable<T>{ 
    private T queue[]; 
    private int head, tail, size; 
    @SuppressWarnings("unchecked") 
    public CircularQueue(){ 
    queue = (T[])new Object[20]; 
    head = 0; tail = 0; size = 0; 
    } 
    @SuppressWarnings("unchecked") 
    public CircularQueue(int n){ //assume n >=0 
    queue = (T[])new Object[n]; 
    size = 0; head = 0; tail = 0; 
    } 
    public synchronized boolean join(T x){ 
    if(size < queue.length){ 
     queue[tail] = x; 
     tail = (tail+1)%queue.length; 
     size++; 
     return true; 
    } 
    else return false; 
    } 
    public synchronized T top(){ 
    if(size > 0) 
     return queue[head]; 
    else 
     return null; 
    } 
    public synchronized boolean leave(){ 
    if(size == 0) return false; 
    else{ 
     head = (head+1)%queue.length; 
     size--; 
     return true; 
    } 
    } 
    public synchronized boolean full(){return (size == queue.length);} 
    public boolean empty(){return (size == 0);} 

    public Iterator<T> iterator(){ 
     return new QIterator<T>(queue, head, size); 
    } 
    private static class QIterator<T> implements Iterator<T>{ 
     private T[] d; private int index; 
    private int size; private int returned = 0; 
     QIterator(T[] dd, int head, int s){ 
      d = dd; index = head; size = s; 
     } 
    public synchronized boolean hasNext(){ return returned < size;} 
    public synchronized T next(){ 
     if(returned == size) throw new NoSuchElementException(); 
     T item = (T)d[index]; 
     index = (index+1) % d.length; 
     returned++; 
     return item; 
    } 
    public void remove(){} 
    } 
} 

당신이 줄 수있는 조언이나 도움을 주시면 감사하겠습니다!

+0

멀티 스레드를 최대한 활용하려면 [잠금 해제] (https://codereview.stackexchange.com/questions/12691/o1-lock-free-container)로 이동하는 것이 좋습니다. – OldCurmudgeon

답변

0

empty()는 동기화되지 않습니다. iterator의 메소드에 동기화하면 반복자 (아마도 쓸모없는)는 보호되지만 큐 자체는 보호하지 않습니다.

+0

큐를 반복자에서 보호하려면 큐를 복제 한 다음 불변 복제를 기반으로 반복기를 만들면됩니까? – user3704648

+0

@ user3704648 확실히, 그것은 트릭을 할 것입니다; 그것은 과잉 일 수 있습니다. –

+0

대신에 조언 해 주시겠습니까? – user3704648

1

empty() 메서드를 동기화하지 못한 것 외에 이터레이터가 호스트 큐와 충분히 격리되어 있지 않습니다. 문제는 iterator 인스턴스에서 동기화되는 메서드가 아니라 사실입니다. 반복기가 호스트 대기열의 데이터 사본을 작성하여 대기열의 스냅 샷을 반복합니다. 이는 좋은 아이디어이며,이 경우 반복자 자체에서 동기화하는 것이 옳은 일입니다.

하지만 완전히 구현하지는 마십시오. 특히, 생성자가 d = dd;을 실행하는 것으로는 충분하지 않습니다. 배열은 객체이므로, 호스트 큐가 사용하는 것과 동일한 배열 객체를 참조하도록 반복자의 배열 참조를 설정합니다. 대신, 해당 배열의 복사본을 만들어야합니다. 이렇게하는 방법은 여러 가지가 있지만 배열의 clone() 메서드를 짧고 간단하게 호출하는 것이 좋습니다.

그런데도 클래스의 메서드 동기화만으로는 방어 할 수없는 스레드 안전 문제가 있습니다. 이들 중 일부는 여러 메소드 호출에 대한 객체의 일관성을 포함합니다. 예를 들어, 하나의 스레드가 큐의 인스턴스에 객체를 대기열에 넣었다고 가정합니다. 그 큐가 쓰레드들간에 공유된다면, 객체를 큐에 넣었던 큐는 나중에 객체를 큐에서 제거 할 수 있다고 가정 할 수 없으며 큐에서 큐를 제거 할 수 있다고 가정 할 수 없다. 그러한 가정을 할 수 있기를 원하면 좀 더 광범위하게 보호를 제공하거나 사용하는 큐 인스턴스가 공유되지 않도록해야합니다.

다른 문제는 실제로 변경할 수있는 경우에 대기열에 포함 된 개체의 변이를 중심으로 이루어집니다. 그 상태는 큐와 iterator의 동기화에 의해 보호되지 않습니다.

+0

자세한 답변을 주셔서 감사합니다. 많이 생각해 주셨습니다. – user3704648