显式锁
1.Lock和ReentrantLock
(1)Lock提供了无条件的,可轮询的,定时的,可中断的锁获取操作(lock接口的实现必须具有与内部加锁相同的内存可见性语义)
(2)ReentrantLock实现了Lock借口,提供了与synchronized互斥和内存可见性的保证
(3)是有lock方法显示获得锁,使用unlock()方法显示释放锁,由此可知,若然忘记释放锁或者没有在发生异常时对锁进行适当的处理很危险
(4)可轮询的和可定时的锁请求:
<1>使用tryLock方法实现(5)可中断的锁获取操作
<2>这样一种方式可以使得在尝试获得锁不成功时,释放已经获得的锁,或者记录失败信息(避免死锁的产生)
<3>使用定时锁,在一定时间内没有获得锁时可以返回,而不会像内部锁一样,会一直阻塞。
<1>lockInterruptibly方法能够使你获得锁,并且响应中断
/** * ReatrantLock实现了显示锁Lock接口 */ public static Lock lock1 = new ReentrantLock(); public static Lock lock2 = new ReentrantLock(); /** * 使用显示锁来避免死锁 如在某一方法的执行需要获取两个锁,就肯能由于两个线程各自持有其中一个锁而引发死锁 但是使用显示锁的话 * 如果或得锁不成功的话就可以作一些处理,而无需向内部锁一样,一直阻塞 这样的话,或得一个锁后若然无法获得第二个锁就会释放 */ public static void testTryLock() { while (true) { if (lock1.tryLock()) { try {//注意了,当成功获得锁后,应该考虑发生异常情况下锁的处理,不然会导致锁没有被释放 if (lock2.tryLock()) { try { //dosomething return; } catch (Exception e) { } finally { lock2.unlock(); } } } catch (Exception e) { } finally { lock1.unlock(); } } try { Thread.sleep(Long.parseLong((int) (Math.random() * 1000) + ""));//随机睡眠一定时间后再次尝试 } catch (InterruptedException ex) { Logger.getLogger(MainTest.class.getName()).log(Level.SEVERE, null, ex); } } } public static Boolean testTryLockInTimes(Long milliseSeconds) { try { if (lock1.tryLock(milliseSeconds, TimeUnit.MILLISECONDS)) { try { //dosomething return true; } catch (Exception e) { } finally { lock1.unlock(); } } } catch (InterruptedException ex) {//定时获得显式锁响应中断? Logger.getLogger(MainTest.class.getName()).log(Level.SEVERE, null, ex); } return false; } /** * 可相应中断的方式获得锁 * * @return */ public static Boolean lockIntruptly() { try { lock1.lockInterruptibly();//当其他线程中断当前线程时,获得锁的过程会中断,并抛出异常,响应中断 try { //dosomething return true; } catch (Exception e) { } finally { lock1.unlock(); } return false; } catch (InterruptedException ex) { Thread.currentThread().interrupt();//恢复中断 return false; } }
(6)对性能的考虑
<1>java5中显式锁提供的竞争上的性能远远优于内部锁,java6中两者的性能接近(7)公平性
2.在synchronized和ReentrantLock之间进行选择<1>reentrantLock构造函数提供了两种公平性的选择 (创建非公平锁或者公平锁)(Semaphore同样提供了公平和非公平的获顺序(收费公平的锁允许闯入,即在锁变为可用时,线程的请求在等待线程的队列中可以跳跃))
<2>公平的获取顺序使得线程的挂起和重新开始的开销更多,性能较差,而很多场景下并不需要保证每一个 线程都按顺序公平获得锁,使用非公平性很多时候可以提供更好的性能
(1)在内部所不能够满足使用时,才应该使用ReentrantLock(java6后两者的性能已经相差不大,并且使用显式锁有一定危险性,忘记unlock等)
(2)java5中线程转存中无法获得显式锁的信息,java6后需要对管理和调试借口进行注册才能使用3.读写锁
(3)未来的性能优化可能更倾向于内部锁而不是显式锁,应为synchronized是内置于jvm的
(1)允许多个读者访问或者允许一个写着访问,两者不能同时进行,即读写锁(这一种允许‘读读’的同步,很多应用个程况下更优)
(2)jdk提供ReentrantReadWriteLock,实现了读写锁