2016-10-11 2 views
2

처리 할 객체가있는 LinkedList가 있습니다. 객체는 다른 스레드에서 객체에 추가되지만 하나의 스레드 만이 객체를 제거/읽습니다. 다른 스레드에서스레드 안전성 문제

private LinkedList<MyObject> queue = new LinkedList<>(); 

new Thread() 
{ 
    @Override 
    public void run() 
    { 
     while (!Thread.interrupted()) 
     { 
      if (!queue.isEmpty()) 
      { 
       MyObject first = queue.removeFirst(); 
       // do sth.. 
      } 
     } 
    } 
}.start(); 

나는 정말 나 자신에게 설명하지 못할

queue.add(new MyObject()); 

때때로이 코드는하지만 예외로 연결 대기열에 객체를 추가합니다. java.util.LinkedList.removeFirst (LinkedList.java:270) 나는이 예외가 왜 만 제거하려고해야하기 때문에 얻을 해달라고

에서 java.util.NoSuchElementException ""스레드 예외 만약 존재한다면 객체.

+1

큐가 비어있을 때 소비자 스레드가 "busy waiting"을 수행합니다. 아마 고쳐야 할 것입니다. –

+0

조금 바보 같지만, 이러한 것들은 예측하기가 매우 어려울 수 있기 때문에 removeFirst 메소드를 호출하는 스레드가 실제로 있다는 것을 확실히하는 것이 좋습니다. if 문 안에 스레드 id의 프린트를 추가하십시오. – TheFooBarWay

+0

@ TheFooBarWay 한두 명의 소비자가 있는지 여부는 중요하지 않습니다. 동기화 문제를 일으키는 데 충분한 여러 개의 생성자가 있습니다. 개체를 수정하는 스레드가 두 개 이상있는 경우 동기화해야합니다. 정확하게 하나의 스레드를 수정하고 여러 스레드가 읽는다면 비동기적일 수 있습니다 (그러나 반복은 까다로울 수 있음). –

답변

2

Nicolas는 이미 언급했듯이 스레드 안전 구현이 필요합니다. LinkedBlockingQueue을 사용하는 것이 좋습니다.

offer 메서드를 사용하여 추가하고 take을 사용하여 제거하면 "대기중인 대기"문제가 해결됩니다. 당신이 무엇을 할 수 있는지

+1

사실 모두 그렇지만 실제 문제는 우리가 예외로 끝나는 방식입니다. 아이템을 제거하는 스레드가 하나뿐이라면 if 문에서 코드를 선형화 할 수 있고 실행이 합법적이어야합니다. – TheFooBarWay

+2

@ TheFooBarWay 구현 세부 사항으로 들어가고 할당이 어디에서 발생하는지 조사하는 것은 가치가 있다고 생각하지 않습니다. 동기화되지 않은 경우 이러한 문제가 발생할 수 있습니다. –

+1

프로그래머의 관점에서 볼 때 아마도 그만한 가치는 없습니다.그러나 이것은 종종 흥미로운 학문적 질문입니다. 제가 생각하기에는 당황 스럽지만, 스레드 안전 솔루션을 제안 할 때 여기서 진짜 질문을 놓친 것처럼 느껴집니다. 계산기를 사용하여 길게 나누는 법을 아이에게 보여주는 것과 같습니다. 당신이 옳다는 것 외. – TheFooBarWay

0

LinkedList입니다 하지 스레드 안전 당신은 그렇지 않으면 당신 때문에 일관성이없는 상태로 이어질 동시 수정이 같은 예측 불가능한 버그에 직면하게 될 것이다 않는은 그래서 당신은 여러 스레드와 공유 할 수 없습니다 사용하는 대신 스레드 안전 dequeConcurrentLinkedDeque입니다.

+0

그건 그렇고, 나는 당신을 downvote하지 않았고, 왜 누군가가 될지 모르겠다. 나는 그것을 취소 upvoted. 사실 Jaroslaw Pawlak의 답변과이 답변 사이에서 선택된 답변을 선택하는 것이 어려웠습니다. 두 가지 모두 매우 도움이 되었기 때문입니다. – BluE

-2

등 뮤텍스, 세마포어, 모니터, 사서함과 같은 스레드를 조정하는 기술의 어떤 종류를 사용하는 것입니다

0

내가 해결하는 방법을 제공하기위한 몇 가지 좋은 솔루션이 있었다 생각하지만 문제는 @BluE가 NoSuchElementException을 보는 이유를 설명하지 못했습니다. 그래서 내가 생각할 수있는 일이 여기에 있습니다.

LinkedList의 액세스는 가능성이 동기화되지 않기 때문에 (! queue.isEmpty())

  1. 가 생산자 스레드가 볼 경우 확인
  2. 두 소비자 스레드가 동시에 큐에 요소를 추가합니다 그렇지 않다.
  3. 두 소비자 스레드가 모두 가서 MyObject를 호출하는 큐에서 요소를 가져 오려고합니다. first = queue.removeFirst();
  4. 대기열에 더 이상 요소가 없으므로 스레드 중 하나가 성공하고 다른 하나는 NoSuchElementException으로 실패합니다.

    는 UPDATE :

하나만 생산자와 한 소비자를 제공, 나는 자바 메모리 모델 사양이 표시되는 행동을 설명 할 수있다 생각합니다.

간단히 말해서 LinkedList에 대한 액세스가 동기화되지 않았기 때문에 JVM에서 제공하는 데이터 가시성 보장이 없습니다.당신이 볼 수 있듯이, 크기와 요소가 다른 변수에 저장됩니다 AbstractCollection의

public boolean isEmpty() { 
    return size() == 0; 
} 

에서 LinkedList의

transient int size = 0; 
transient Node<E> first; 

// ... 

public int More ...size() { 
    return size; 
} 

// ... 

public E removeFirst() { 
    final Node<E> f = first; 
    if (f == null) 
     throw new NoSuchElementException(); 
    return unlinkFirst(f); 
} 

에서

:의이 IsEmpty 함수의 구현 및 removeFirst와 방법을 살펴 보자 . 따라서 소비자 스레드가 "크기"변수에서 업데이트를보고 "첫 번째"에서 업데이트를 볼 수없는 경우 일 수 있습니다.

+1

추측 해 주셔서 감사합니다.하지만 한 소비자 만 있기 때문에 그럴 수는 없습니다. – BluE