2010-11-18 2 views
2

xml 파싱을 수행하는 함수가 있습니다. 나는 함수 thread를 안전하게하고 싶지만 가능한 한 최적화 된 (블로킹을 줄인다).dom apis에서 java multithreading

public Document doXML(InputStream s) 
{ 
//Some processing. 
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
    DocumentBuilder parser = factory.newDocumentBuilder(); 
    Document xmlDoc = parser.parse(is); 
    return xmlDoc; 

} 

을하지만 각각의 호출에 새의 DocumentBuilderFactory 또는 DocumentBuilder를 만들지 않으려 다음과 같이 짧은 코드에서
는 무언가이다.
팩토리와 파서를 다시 사용하고 싶지만 스레드로부터 안전하지는 않습니다. 그래서 가장 최적의 접근 방식은 무엇입니까?
1) 클래스 필드에 DocumentBuilderFactory를 캐시하고 factory.newDocumentBuilder()를 동기화합니다. 각 스레드는 DocumentBuilderFactory를 DocumentBuilder를 와 동기화 parser.parse (IS) 자체의 DocumentBuilder
인스턴스 2) 캐시를 갖도록; 스레드 당

(2)가 가장 좋지만 안전하게 할 수 있을까요? 또한 동기화에 의한 차단을 피할 수 있습니까? 가능한 한 빨리 보내고 싶습니다.

고마워요?

+0

잘 모르겠습니다. 이 코드 샘플에서는 synchronized 메소드를 사용할 필요가 없습니다. 이후로는 리소스를 보호 할 공유 객체가 없습니다. 공유 된 공통 객체가있는 경우 상호 배제하기 위해 공통 객체를 동기화해야합니다. –

+0

@Mohamed Saligh : DocumentBuilder와 DocumentBuilderFactory를 공유하려고합니다. 지금 당장은 아무 문제가 없습니다. – Cratylus

답변

4

스레드 풀에서와 같이 스레드를 재사용하는 경우 DocumentBuilderFactory를 스레드 로컬로 선언 할 수 있습니다. 각 스레드에 대해 새로운 세트를 작성하는 오버 헤드가 있지만, 제가 말했듯이, 당신이 reuising하는 경우, 후속 오버 헤드가 매우 낮습니다.

final ThreadLocal<DocumentBuilderFactory> documentBuilderFactor = new ThreadLocal<DocumentBuilderFactory>(){ 
    public DocumentBuilderFactory initialValue(){ 
     return DocumentBuilderFactory.newInstance(); 
    } 
} 

public Document doXML(InputStream s) 
{ 
//Some processing. 
    DocumentBuilderFactory factory = documentBuilderFactor.get(); 
    DocumentBuilder parser = factory.newDocumentBuilder(); 
    Document xmlDoc = parser.parse(is); 
    return xmlDoc; 

} 

여기서 각 스레드마다 하나의 DocumentBuilderFactory 만 만듭니다.

구문 분석 할 때 DocumentBuilder가 스레드로부터 안전한지 여부는 알지 못합니다 (변경 불가능합니까?). 그러나 구문 분석 할 때 DocumentBuilder가 쓰레드에 안전하다면, 내가 말한 것과 같은 메커니즘을 사용할 수있다.

이 해상도는 가능한 한 빠른 속도로 전체 처리량을 만듭니다.

참고 :이 테스트를 거치지 않았거나 컴파일 된 것은 내가 언급 한 것에 대한 아이디어를 제공합니다.

+0

@ John V. : 나는이 기능을 (다른 사람들과 함께) 제공해야한다. 쓰레드는이 함수를 호출 할 것이지만, 쓰레드가 어떻게/언제 만들어 질지 (내 부분이 아님)는 제어 할 수 없다. 그래서 스레드를 재사용 할 것인지 여부를 알 수 없습니다. 따라서 스레드를 재사용하지 않으면 코드가 원래 게시물과 동일합니까? – Cratylus

+0

네, 맞습니다. 스레드를 재사용하지 않는다면 ThreadLocal을 새로운 DOM 팩토리 생성과 함께 사용하는 오버 헤드가 조금 높을 것입니다. 그럼 다시 생각해 볼 때마다 새로운 스레드를 시작하는 것이 더 큰 문제입니다 :) –

+0

@ John V .: 맞습니다!하지만 스레드를 풀링하고 로컬 스레드를 사용한다면 누출이있을 수 있습니까? 이것을 확인하십시오 : http://weblogs.java.net/blog/jjviana/archive/2010/06/09/dealing-glassfish-301-memory-leak-or-threadlocal-thread-pool-bad-ide – Cratylus

1

동기화 차단을 피하려면 원자 작업을 사용해야합니다. javax.xml.parser.*의 동작은 구현에 따라 다릅니다 (시스템 속성을 사용하여 구현을 지정하거나 구현 코드를 호출 할 수 있음). 스레드 수와 각 스레드의로드 가중치에 따라 파서 개체 생성을 제어하는 ​​것이 합리적 일 수 있습니다. 새 파서 작성 또는 파서를 기다려야합니다. 이 코드는 시작할 때 파서 풀을 만들 수 있으며 풀은 파서를 풀에서 가져옵니다. 파서는 무료 파서가 없을 때 차단합니다. 스레드가 파서를 획득하면 데이터를 구문 분석하고 파서를 다시 설정하여 풀로 되돌립니다. 풀의 길이에 따라 시간/메모리 사용을 항상 제어 할 수 있습니다.

+0

@khachik : "원자력 작전을 사용하는지 확인하십시오"에 대해 자세히 설명해 주시겠습니까? 나는 당신이 무엇을 의미하는지, 그리고 그것을 막기 위해 어떻게해야하는지 모르겠다. – Cratylus

+0

@ user384706 글쎄, "원자"는 혼란 스러울 수 있기 때문에 XML 파싱에 가장 적합한 단어는 아니다. (일반적으로 원자는 원자 적으로 수행 할 수있는 연산을 의미한다. 하드웨어 계층 (Compare-And-Swap) 첫 문장은 두 스레드에서 동일한 구문 분석기를 사용할 수 없다는 의미입니다. 따라서 하나의 스레드에서만 파서가 사용되도록해야합니다. – khachik

+0

@khachik : 그러나 풀 크기를 결정하는 방법은 무엇입니까? 가장 좋은 스레드 수는 numberOfProcessors + 1입니다.이 값이 내 풀에 맞는 크기입니까? – Cratylus

2

2) 스레드로부터 안전하지만 앱은 한 번에 하나의 문서 만 구문 분석 할 수 있습니다.

코드를 사용하지 않는 이유는 무엇입니까? 합니까

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
DocumentBuilder parser = factory.newDocumentBuilder(); 

명백하게 허용 할 수없는 오버 헤드가 있습니까?

+0

가벼운 테스트를 통해 문제가 발생하지 않습니다. 하지만로드에 따라 더 나빠지는지 확실하지 않습니다. 독서에서, 나의 이해는 공장을 창조하는 것은 너무 비싸다이다. 나는 이것이 DocumentBuilder의 약자라고 생각한다. 그래서이 코드가 궁극적으로 성능이 좋지 않을지 궁금합니다. – Cratylus

+0

팩토리를 생성 할 때 * 약간의 오버 헤드가 있지만 스트림에서 읽은 후 문서를 파싱하는 것과 비교할 때 그다지 중요하지 않습니다. 대부분의 오버 헤드는 시스템 속성 인 jaxb.properties (한 번만 캐시하고 어쨌든 캐시)와 마지막으로 META-INF/서비스를 검사하여 구현 클래스의 이름을 찾는 것처럼 보입니다. 구현 클래스 이름을 지정하여 오버 헤드를 줄일 수 있습니다. – Qwerky

+0

흥미 롭습니다. 하지만 구현 클래스를 어떻게 알 수 있습니까? John M의 답변과 비슷한 것을 제안하고 있습니까? 내 코드가 실행될 런타임에 사용할 수있는 클래스 이름을 어떻게 알 수 있습니까? – Cratylus

1

비슷한 상황에서 성능 문제가 발생했습니다. 스레드 문제 (초당 10 개)를 피하기 위해 각 사용마다 팩토리 객체를 작성했습니다.그 (오래 된) 플랫폼에서의 XML 구현은 서비스 공급자 클래스에 대해 상대적으로 느린 검색 로직을 수행했습니다.

내 조정은 결과를 결정하고 명령 줄 속성을 통해 구성했습니다. 이로 인해 조회가 건너 뜁니다.

-Djavax.xml.parsers.DocumentBuilderFactory=com.example.FactoryClassName 
-Djavax.xml.transform.TransformerFactory=com.example.OtherFactoryClassName 

실망스러운 점은 클래스가 발견되면 룩업 코드에 캐싱 로직이 있다는 것입니다. 하지만 미스 캐싱 (아무것도 찾을 수 없습니다, 기본값 사용). 부정적인 경우를 처리하는 약간 더 나은 조회 캐싱은 불필요하게 만들었을 것입니다.

그래도 필요합니까? 사용자 환경에서 테스트해야합니다. Solaris에서 truss를 사용하여 조회 논리로 인해 자주 발생하는 파일 작업을 확인했습니다.

+0

이 "트릭"은 특정 플랫폼에서만 작동합니다. 그렇다면 내 코드가 실행될 머신에서 실제 팩토리 구현 클래스가 무엇인지 어떻게 알 수 있습니까? – Cratylus

+0

필자의 경우, 소스 코드를 읽음으로써 기본 응답을 알아 냈습니다. 그러나 첫 번째 단계는 당신이 이런 문제를 겪었는지 확인하는 것입니다. 당신이 진짜 이득을 얻지 않는 한 이런 식의 하드 코딩을 사용하지 마십시오. –

+0

내가 제안한 것과 같은 것이 필요한지 확인하기 위해이 문제를 벤치마킹하는 것에 대한 제안 사항이 있으십니까? – Cratylus