最近自己打算实现一个比较原始但是可以自动释放的锁的管理类,基本想法是根据一个对象id(整数)得到一个锁,同一时刻只能有一个锁的实例对应该id,并且当内存内没有被外部对象持有该id对应的锁时,无论该锁是不是锁定状态该锁的实例都可以被自动释放掉。
代码大概如下:
然后我写了个测试:
请注意,测试类的第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,
代码大概如下:
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}