论坛首页 Java企业应用论坛

JDK1.6.0_20下一个应该是BUG的运行时问题

浏览 2368 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-12-28  
最近自己打算实现一个比较原始但是可以自动释放的锁的管理类,基本想法是根据一个对象id(整数)得到一个锁,同一时刻只能有一个锁的实例对应该id,并且当内存内没有被外部对象持有该id对应的锁时,无论该锁是不是锁定状态该锁的实例都可以被自动释放掉。

代码大概如下:
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class AutoReleaseLock implements Lock {
	private static transient Map<Integer, Lock> objectLocks = new WeakHashMap<Integer, Lock>(32);

	public static synchronized Lock getObjectLock(int id) {
		//不能使用JDK自动装箱的功能,128以内的数会被JDK缓存住
		Integer key = new Integer(id);
		Lock lock = objectLocks.get(key);
		if (lock == null) {
			lock = new ReentrantLock();
			objectLocks.put(key, lock);
		} else {
			//在导出的lock实现中使用map中的key,防止该锁存在时该对象被回收
			for (Map.Entry<Integer, Lock> entry : objectLocks.entrySet()) {
				if (key.equals(entry.getKey())) {
					key = entry.getKey();
				}
			}
		}
		return new AutoReleaseLock(key, lock);
	}

	Integer objectID;
	Lock lockImplement;

	private AutoReleaseLock(Integer id, Lock impl) {
		this.objectID = id;
		lockImplement = impl;
	}

	public void lock() {
		lockImplement.lock();

	}

	public void lockInterruptibly() throws InterruptedException {
		lockImplement.lockInterruptibly();

	}

	public Condition newCondition() {
		return lockImplement.newCondition();
	}

	public boolean tryLock() {
		return lockImplement.tryLock();
	}

	public boolean tryLock(long time, TimeUnit unit)
			throws InterruptedException {
		return lockImplement.tryLock(time, unit);
	}

	public void unlock() {
		lockImplement.unlock();
	}
}


然后我写了个测试:
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.locks.Lock;

import junit.framework.TestCase;

public class AutoReleaseLockTest extends TestCase {
	
	public void testObjectLock() throws Exception {
		ExecutorService service = Executors.newCachedThreadPool();
		
		Lock lock = AutoReleaseLock.getObjectLock(1);
		assertNotNull(lock);
		lock.lock();

		System.gc();
		Thread.sleep(1);
		System.gc();

		// JDK1.6 BUG在这里
		//System.err.println(lock);
		Future<?> feature = service.submit(new Runnable() {
			public void run() {

				Lock lock2 = AutoReleaseLock.getObjectLock(1);
				assertEquals(lock2.tryLock(), false);
			}
		});
		feature.get();
		Thread.sleep(100);

		lock = AutoReleaseLock.getObjectLock(1);
		assertNotNull(lock);

		System.gc();
		Thread.sleep(1);
		System.gc();
		feature = service.submit(new Runnable() {
			public void run() {
				Lock lock2 = AutoReleaseLock.getObjectLock(1);
				assertEquals(lock2.tryLock(), false);
			}
		});
		feature.get();

		lock.unlock();
		feature = service.submit(new Runnable() {
			public void run() {
				Lock lock2 = AutoReleaseLock.getObjectLock(1);
				assertEquals(lock2.tryLock(), true);
				lock2.unlock();
			}
		});
		feature.get();


		lock.lock();
		lock = null;
		System.gc();
		//GC一定要来哦
		Map<Integer, byte[][]> temp = new HashMap<Integer, byte[][]>();
		for (int i = 0; i < 64; i++) {
			temp.put(new Integer(i), new byte[1024][1024]);
		}
		Thread.sleep(1);
		System.gc();
		feature = service.submit(new Runnable() {
			public void run() {
				Lock lock2 = AutoReleaseLock.getObjectLock(1);
				assertNotNull(lock2);
				assertTrue(lock2.tryLock());
				lock2.unlock();
			}
		});
		feature.get();
		System.gc();
		//GC一定要来哦
		for (int i = 0; i < 64; i++) {
			temp.put(new Integer(i), new byte[1024][1024]);
		}
		Thread.sleep(1);
		System.gc();

		//release
		for (int i = 0; i < 100000; i++) {
			lock = AutoReleaseLock.getObjectLock(i);
			try {
				assertTrue(lock.tryLock());
			} finally {
				lock.unlock();
			}
		}
	}
	
	public void test10Times()throws Exception{
		System.err.println(System.getProperties());
		for(int i=0;i<10;i++){
			System.err.println(i);
			testObjectLock();
		}
	}
}

请注意,测试类的第24行(System.err.println(lock);)最开始是没有的,因为最开始开发是在JDK1.5下开发,测试一切正常。突然有一天我们升级到JDK1.6,居然发现我们的测试用例跑不过了。经过百般的探索,发现只要运行第3遍testObjectLock,test类的第29行处就会报错,后台打印输出居然发现objectLocks 是空的,也就是说objectLocks 里对象被释放掉了,那么最外部的lock对象呢?

于是我加上了第24行,这样在JDK1.6下测试也顺利通过了。但是一旦把第24行注掉,test10Times中运行第3次testObjectLock时就会在29行处出错。

我初步怀疑是JDK的JIT在进行优化时,认为在29行处lock所指向实例已经没人使用就释放掉了,而objectLocks 也因此被清空,导致test通不过。欢迎各位的高见。

我的操作系统是windows7 32位好像是英文版,改成的中文界面,JDK 1.6.0-20,
{java.runtime.name=Java(TM) SE Runtime Environment, sun.boot.library.path=C:\Program Files\Java\jdk1.6.0_20\jre\bin, java.vm.version=16.3-b01, java.vm.vendor=Sun Microsystems Inc., java.vendor.url=http://java.sun.com/, path.separator=;, java.vm.name=Java HotSpot(TM) Client VM, file.encoding.pkg=sun.io, sun.java.launcher=SUN_STANDARD, user.country=CN, sun.os.patch.level=, java.vm.specification.name=Java Virtual Machine Specification, user.dir=D:\workspace\sources\csvformater, java.runtime.version=1.6.0_20-b02, java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment, java.endorsed.dirs=C:\Program Files\Java\jdk1.6.0_20\jre\lib\endorsed, os.arch=x86, java.io.tmpdir=C:\Users\pengzy\AppData\Local\Temp\, line.separator=
, java.vm.specification.vendor=Sun Microsystems Inc., user.variant=, os.name=Windows 7, sun.jnu.encoding=GBK, java.library.path=..., java.specification.name=Java Platform API Specification, java.class.version=50.0, sun.management.compiler=HotSpot Client Compiler, os.version=6.1, user.home=C:\Users\pengzy, user.timezone=, java.awt.printerjob=sun.awt.windows.WPrinterJob, file.encoding=GBK, java.specification.version=1.6, java.class.path=D:\workspace\sources\csvformater\bin;D:\workspace\sources\csvformater\lib\jxl-2.5.7.jar;D:\HCIT_SDK_3.4\eclipse\plugins\org.junit_3.8.2.v20080602-1318\junit.jar;/D:/HCIT_SDK_3.4/eclipse/configuration/org.eclipse.osgi/bundles/751/1/.cp/;/D:/HCIT_SDK_3.4/eclipse/configuration/org.eclipse.osgi/bundles/752/1/.cp/, user.name=pengzy, java.vm.specification.version=1.0, java.home=C:\Program Files\Java\jdk1.6.0_20\jre, sun.arch.data.model=32, user.language=zh, java.specification.vendor=Sun Microsystems Inc., awt.toolkit=sun.awt.windows.WToolkit, java.vm.info=mixed mode, sharing, java.version=1.6.0_20, java.ext.dirs=C:\Program Files\Java\jdk1.6.0_20\jre\lib\ext;C:\Windows\Sun\Java\lib\ext, sun.boot.class.path=C:\Program Files\Java\jdk1.6.0_20\jre\lib\resources.jar;C:\Program Files\Java\jdk1.6.0_20\jre\lib\rt.jar;C:\Program Files\Java\jdk1.6.0_20\jre\lib\sunrsasign.jar;C:\Program Files\Java\jdk1.6.0_20\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.6.0_20\jre\lib\jce.jar;C:\Program Files\Java\jdk1.6.0_20\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.6.0_20\jre\classes, java.vendor=Sun Microsystems Inc., file.separator=\, java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport.cgi, sun.io.unicode.encoding=UnicodeLittle, sun.cpu.endian=little, sun.desktop=windows, sun.cpu.isalist=pentium_pro+mmx pentium_pro pentium+mmx pentium i486 i386 i86}
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics