2009-09-29 2 views
0

내 프로그램을 제대로 작동 시키는데 어려움을 겪고 있습니다. 간단히 말해, 내 프로그램은 Main, MessageReceiver, Scheduler (Quartz 패키지 사용), Scheduler 스레드에 의해 스케줄 된 두 가지 유형의 스레드 (TraceReader 및 ScheduledEvent)와 같은 몇 가지 초기 스레드로 구성됩니다. 이제 TraceReader가 시작되면 특수 추적 파일을 읽고 시작 시간, 반복 간격 (500ms ~ 1 초) 및 종료 시간으로 이벤트를 예약합니다. 현재, 약 140 개의 이벤트가 동시에 발생하여 여러 ConcurrentModificationException 오류가 발생합니다. 이제 몇 가지 코드 : ConcurrentModificationExceptions을 피하기위한 Java 동기화?

public class Client { //main class 
public static volatile HashMap<Integer, Request> requests; 
public static class Request{ 
    String node; 
    int file_id; 
    long sbyte; 
    int length; 
    int pc_id; 
    public Request(){ 

    } 
} 

public static synchronized void insertRequest(int req_nr, String node, int file_id, long sbyte, int length, int pc_id) { 
    Request tempr = new Request(); 
    tempr.node = node; 
    tempr.file_id = file_id; 
    tempr.sbyte = sbyte; 
    tempr.length = length; 
    tempr.pc_id = pc_id; 
    requests.put(req_nr, tempr);      
} 

public static synchronized void doSynchronized(int req_nr, String node, int file_id, long sbyte, int length, int pc_id) {   
    reqnr++; 
    String r = "P" + reqnr + "," + file_id + "," + Long.toString(sbyte) + "," + length;   
    insertRequest(Client.reqnr, node, file_id, sbyte, length, pc_id);   
} 

public class ScheduledEvent implements Job { 
public synchronized boolean isRequested(long sbyte, int length, int file_id, String node) { 
    Request req; 
    Iterator<Integer> it = Client.requests.keySet().iterator(); 
    while (it.hasNext()) { 
     req = Client.requests.get(it.next()); 
     if (req.node.equals(node) && req.file_id == file_id && hits(req.sbyte, req.length, sbyte, length)) { 
      return true; 
     } 
    } 
    return false; 
} 
} 

그래서 나는 기본적으로 ScheduledEvent 클래스의 isRequested 방법에 대한 오류를 얻을. 100 개 이상의 동시 스레드가 있기 때문에 다른 스레드가 isRequested 메서드에서 요청 개체를 반복하려고 시도하는 동안 다른 스레드가 Client.doSynchronized()를 사용하고 있다는 사실 때문에 오류가 발생했다고 생각합니다. 차단 (Thread.join() 등)을 사용하지 않고 해당 객체에 액세스 할 수있는 스레드를 만들 수있는 방법이 있습니까?

+0

두 가지 스타일 포인트 : 요청의 구성원은 '비공개'이어야하고 게터와 설정자가 추가되어야합니다. 요청은 '비공개'여야합니다. 왜'정적'메소드를 사용하고 계십니까? 그들은 당신이 멋지고 확장 가능한 무언가를 만드는 데 도움이되지 않을 것입니다 .. – pjp

+0

주셔서 감사합니다. Java 프로그래밍에 익숙하지 않고 이러한 키워드의 기능을 이해하지 못합니다. 정적 메소드의 대부분을 가지고있는 이유는 이전에 "정적이 아닌 메소드의 정적 액세스"오류가 발생했기 때문입니다. – Azimuth

+0

요청을 비공개로 선언 할 경우 다른 클래스 (예 : ScheduledEvent)에서 액세스 할 수 없습니다. 그렇지 않습니까? – Azimuth

답변

5

코드의 기본 문제는 개체가 다른 개체 (잠금)에서 동기화된다는 것입니다. 대신 동기화 방법을 선언,이 문제를 해결하려면 - 클래스의 객체를 동기화하는 - 객체 자체 요청에 동기화 : 당신은 ConcurrentHashMapHashMap을 대체 할 수

public class Client { //main class 

public static void insertRequest(int req_nr, String node, int file_id, long sbyte, int length, int pc_id) { 
    ... 
    synchronized (requests) { 
     requests.put(req_nr, tempr); 
    } 
    ... 
} 

public class ScheduledEvent implements Job { 
public boolean isRequested(long sbyte, int length, int file_id, String node) { 
    ... 
    synchronized (requests) { 
     while (it.hasNext()) { 
      req = Client.requests.get(it.next()); 
      if (req.node.equals(node) && req.file_id == file_id && hits(req.sbyte, req.length, sbyte, length)) { 
      return true; 
     } 
     } 
    } 
    ... 
} 
} 
+0

나는 그것을 시도했지만 그것은 "비 최종 필드 동기화"라고 말합니다. – Azimuth

+0

최종 요청 선언; 당신은 아마도 어쨌든 그 참조를 변경하지 않을 것입니다. – Zed

+0

최종적으로'public final static volatile HashMap 요청; ' – pjp

1

.

http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ConcurrentHashMap.html#keySet%28%29

keySet()이 맵에 포함되는 키 의 셋트 뷰를 돌려줍니다. 지도에 의해 지원되는 집합은 이므로 지도의 변경 사항은 집합에 반영되고 의 변경 사항은 집합에 반영됩니다. 이 세트는 제거를 지원하며이 맵에서 해당 매핑을 제거하고 Iterator.remove, Set.remove,removeAll, retainAll 및 clear 작업을 통해 을 제거합니다. add 또는 addAll 조작은 지원하지 않습니다. 뷰의 반환 된 반복자는 투사 해, ConcurrentModificationException, 및 보증 그들이 반복자의 구축시에 존재 으로 요소를 통과 결코 「약 일치 "반복자이며, (다만 보증 없는) 어떤 을 반영 건설 이후의 수정.

ConcurrentModificationException은 맵셋에서 반복자의 빠른 패스트 특성으로 인해 발생합니다.

+0

답장을 보내 주셔서 감사합니다. 하지만 ConcurrentHashMap은 HashMap보다 속도가 느린다고 들었습니다. 그것이 사실인지 아십니까? – Azimuth

+1

HashMap을 "동기화"하는 것보다 확실히 빠를 것입니다. 이는 ConcurrentHashMap이 전체 맵에 대해 하나의 잠금 대신 HashMap의 개별 버킷에 더 세밀한 레벨 잠금을 가지고 있기 때문입니다. 그러나 귀하의 코드는 값에 대한 선형 검색과 같은 작업을 수행하므로 어쨌든 그렇게 빠르지는 않을 것입니다. – pjp

+0

힌트를 보내 주셔서 감사합니다. 내 모든 코드를 편집해야 할 것 같습니다. 하지만 어쨌든 완료되어야합니다 ... – Azimuth

1

아마도 HashMap 대신 ConcurrentHashMap을 사용해야합니다. 이렇게하면 많은 수동 동기화를 제거 할 수 있습니다.

UPDATE :

  1. ScheduledThreadPoolExecutor 당신이 무엇을해야합니까 :

    다시 질문을 보면, 나는 질문/제안의 몇 가지있다? 가능한 경우 java.util.concurrent에 제공된 상위 수준의 동시성 구조를 사용하는 것이 좋습니다. 왜냐하면 기본 동시성 프리미티브로 시작할 때 동시성을 얻기가 너무 어렵 기 때문입니다.

  2. 이전 질문에 대한 대답이 '아니오'라고 가정하면 요청 번호에 맞춰 Map 대신 Queue을 사용하는 고전적인 생산자 - 소비자 패턴을 사용할 수없는 이유가 있습니까? BlockingQueue 구현 중 하나가이를 위해 이상적이라고 생각됩니다.
+0

ConcurrentHashMap은 인스턴스 내에서 작업을 동기화하지만, 동시에 다른 인스턴스가 동일한 인스턴스 (예 : thread1 호출 맵)에서 다른 작업을 호출하지 못하도록 보장 할 방법이 없습니다. add() while thread2. 그것을 반복합니다. –

+0

thread2의 iterator는 thread1이 값을 추가하기 전에'keySet'의 스냅 샷을 보게됩니다. – pjp

+0

Quartz 라이브러리 (www.opensymphony.com/quartz/)를 사용하기 때문에 ScheduledThreadPoolExecutor가 있는지 몰랐습니다. ScheduledThreadPoolExecutor로 전환하고 싶다면 3 가지 프로그램을 모두 편집해야합니다 ... 그래서 Quartz를 고수하고 싶습니다. 질문 2에 대한 대답은 명확하게 이해하지 못했지만 요청 번호가 필요합니다. 왜냐하면이 프로그램이 네트워크를 통해 다른 프로그램으로 요청을 보내므로 프로젝트에서 프로 시저 호출을 사용하지 않기 때문입니다. – Azimuth

1

귀하는 A) 휘발성 키워드 사용에 대한 오해와 B) 동기화 사용 오해가 있습니다. 이것은 까다로운 주제가 될 수 있으므로 두 가지를 간략하게 설명해 보겠습니다. 휘발성

변수가 여러 스레드에 의해 업데이트 될 수 있습니다 언급되는 컴파일러를 알려줍니다, 그래서 그것은/변수에 기록 읽을 때 각 스레드가 정확한 값을 볼 수 있는지 확인해야합니다. 휘발성을 잘못 사용하는 이유는 휘발성 오브젝트을 선언하면 컴파일러에게 참조이 volatile입니다. 즉, HashMap에 대한 포인터가 언제든지 변경 될 수 있습니다 (예 : 요청이 새 HashMap() 인 경우 포인터가 변경되었을 수 있음). 그러나 프로그램에서 언제든지 요청에 대한 참조를 변경하지 않으므로 변동성이 있다고 선언 할 수 없으며 다른 대답에서 언급했듯이 최종 선언을해야합니다. 동기화

은 본질적으로 어떤 객체에 대한 잠금()에 대한 바로 가기입니다 키워드입니다. 클래스의 인스턴스 컨텍스트 내에서 synchronized를 사용하면 synchronized는 인스턴스에서 을 잠급니다. 즉 :

class X { 
    public synchronized doStuff() { 
    ... 
    } 
} 

X instance = new X(); 
instance.doStuff(); 

과 같이 동일한 효과가있을 것이다 : 당신은 그러나 정적 맥락에서 동기화를 사용하는 경우

class X { 
    public doStuff() { 
    lock(this) { // or lock(instance) 
     ... 
    } 
    } 
} 

가 잠금 동기화에 의해 생성은에 고정 할 인스턴스가 없기 때문에 대신는 것이다 클래스 유형의 인스턴스를 잠급니다. 단순화하기 위해 synchronized 문을 사용하는 모든 클래스에 대해 정적 컨텍스트에서 동기화하면 모든 경우에 잠금 1이 잠기고 비 정적 컨텍스트에서 동기화되면 매번 잠금 2가 잠 깁니다. 즉, 동일한 잠금을 사용하는지 확인하지 않는 한 멀티 스레딩 안전성이 없습니다입니다.

코드 작업을 수행하는 한 가지 방법은 lock (요청) 문을 명시 적으로 사용하여 모두 동일한 잠금을 공유하는 것입니다. 실제로 사용하려는 개체를 실제로 잠그는 것이 항상 최선의 방법은 아니지만 대개 사람들은 잠글 수있는 새로운 개체를 만들고 혼동을 피하기 위해 다른 용도로 사용하지 않습니다.

다른 응답은 ConcurrentHashMap을 사용하여 언급했지만,이 답변은 유효한 해결책이지만 여기서의 근본적인 문제는 해결하지 못합니다. 그러나 ConcurrentHashMap을 사용하기로 결정한 경우, ConcurrentHashMap이 어떤 안전 보장인지 이해하고 동시 수정에 관해서는 알려주지 마십시오. 항상 기대 한대로는 아닐 수도 있습니다.