민서네집

java.util.ConcurrentModificationException is thrown when using Spring AOP 본문

Spring

java.util.ConcurrentModificationException is thrown when using Spring AOP

브라이언7 2013. 12. 11. 00:58

java.util.ConcurrentModificationException is thrown when using Spring AOP


http://forum.spring.io/forum/spring-projects/aop/122321-java-util-concurrentmodificationexception-is-thrown-when-using-spring-aop


지금 프로젝트에서 Spring AOP 3.1.2.RELEASE 와 AspectJ 1.7.3 버전을 사용하고 있는데, Spring AOP를 걸었을 때

아주 가끔(아마도 여러 명의 유저가 동시접근하는 멀티 쓰레드 환경에서)

ConcurrentModificationException 이 발생한다.

- 정확히 말하면 aspectjtools.jar 가 1.7.3 버전이다.


https://bugs.eclipse.org/bugs/show_bug.cgi?id=408721


위 웹페이지를 보니 AspectJ 1.7.1 버전에서 에러가 발생해서, 


fixed. I put synchronized blocks around access to that collection.


1.7.4 버전에서 수정되었다고 나와 있다.


Error 재현해 보기


aspectjtools-1.7.3.jar 의 소스를 풀어서

그 중에서 ReferenceType.java 파일의 소스를 풀어서

src 디렉터리에 패키지 경로 그대로 갖다 놓는다.


/**
 * Look for a derivative type with the specified type parameters.  This can avoid creating an
 * unnecessary new (duplicate) with the same information in it.  This method also cleans up
 * any reference entries that have been null'd by a GC.
 *
 * @param typeParameters the type parameters to use when searching for the derivative type.
 * @return an existing derivative type or null if there isn't one
 */
public ReferenceType findDerivativeType(ResolvedType[] typeParameters) {
	synchronized (derivativeTypes) {
		List<WeakReference<ReferenceType>> forRemoval = new ArrayList<WeakReference<ReferenceType>>();
		for (WeakReference<ReferenceType> derivativeTypeRef: derivativeTypes) {
			 //////////////// TEST START //////////////////////
	        //java.util.Random random = new java.util.Random();
	        /*try
	        {
	            //Thread.sleep(random.nextInt(2000));
	            Thread.sleep(2000);
	        } catch (InterruptedException e)
	        {
	            e.printStackTrace();
	        }*/
	        //////////////// TEST END //////////////////////
			ReferenceType derivativeType = derivativeTypeRef.get();
			if (derivativeType==null) {
				forRemoval.add(derivativeTypeRef);
			} else {
				if (derivativeType.isRawType()) {
					continue;
				}
				if (equal(derivativeType.typeParameters,typeParameters)) {
					return derivativeType; // this escape route wont remove the empty refs
				}
			}
		}
		derivativeTypes.removeAll(forRemoval);
	}
	return null;
}


TEST START 과 TEST END 사이를 주석 처리해야지 처음에 Tomcat 서버가 뜬다. 아니면 뜨는데 시간이 너무 오래 걸림.

Tomcat 서버가 시작하면 TEST 부분의 주석을 풀고, 여러 브라우저를 띄워서 반복적으로 request 를 날려본다.

그러면 java.util.ConcurrentModificationException 이 매우 자주 발생한다.


중간에 주석을 풀어야 하므로, 이클립스에서 Tomcat 서버를 debug 모드로 실행해야 한다.

그리고, Web Modules 항목에서 Auto Reload 옵션을 Disabled 로 해야지, 변경 사항이 발생해도 Tomcat 서버가 재시작하지 않고 바로 변경사항이 적용된다.


이번에는 aspectjtools-1.7.4.jar 로 jar 를 업그래이드 해서 /WEB-INF/lib 폴더에 넣고, aspectjtools-1.7.4-sources.jar 파일도 받아서 압축을 풀고, findDerivativeType() 메서드를 찾아서 위와 같이 테스트 코드를 넣고, 동일하게 테스트를 해 본다.


소스를 보면 synchronized (derivativeTypes) { ... } 로 

for (WeakReference<ReferenceType> derivativeTypeRef: derivativeTypes) { ... } 를 감싼 것을 알 수 있다.


ConcurrentModificationException 은 더 이상 발생하지 않는다.


Comments