synchronized只有等当前线程执行完,才能释放锁,或者虚拟机出错时释放资源。html
synchronized可能形成其它线程长时间的等待,好比获取锁的线程在执行耗时的IO操做,那么其它须要该资源的线程只能长时间的等待。java
synchronized没法知道当前线程获取锁是否成功。缓存
1:基本概念多线程
Lock 实现提供了比使用 synchronized 方法和语句可得到的更普遍的锁定操做。此实现容许更灵活的结构,能够具备差异很大的属性,能够支持多个相关的 Condition 对象。并发
锁是控制多个线程对共享资源进行访问的工具。一般,锁提供了对共享资源的独占访问。一次只能有一个线程得到锁,对共享资源的全部访问都须要首先得到锁。不过,某些锁可能容许对共享资源并发访问,如 ReadWriteLock 的读取锁。高并发
Lock 接口的实现容许锁在不一样的做用范围内获取和释放,并容许以任何顺序获取和释放多个锁工具
锁定和取消锁定出如今不一样做用范围中时,必须谨慎地确保保持锁定时所执行的全部代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁。性能
Lock 实现提供了使用 synchronized
方法和语句所没有的其它功能,包括提供了一个非块结构的获取锁尝试 (tryLock()
)、一个获取可中断锁的尝试 (lockInterruptibly()
) 和一个获取超时失效锁的尝试 (tryLock(long, TimeUnit)
)。ui
全部 Lock 实现都必须 实施与内置监视器锁提供的相同内存同步语义。spa
2:Lock接口
public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }
lock()方法是日常使用得最多的一个方法,就是用来获取锁。若是锁不可用,出于线程调度目的,将禁用当前线程,而且在得到锁以前,该线程将一直处于休眠状态。
Lock 实现可能可以检测到锁的错误使用,好比会致使死锁的调用,在那种环境下还可能抛出一个 (unchecked) 异常。Lock 实现必须对环境和异常类型进行记录。1
通常的格式为:记住lock使用结束后必定要释放。
Lock lock = ...; lock.lock();try{ doSomeThing(); }catch(Exception ex){ }finally{ lock.unlock(); }123456789
boolean tryLock()方法
仅在调用时锁为空闲状态才获取该锁。若是锁可用,则获取锁,并当即返回值 true。若是锁不可用,则此方法将当即返回值 false。此用法可确保若是获取了锁,则会释放锁,若是未获取锁,则不会试图将其释放。
经常使用使用格式为:
Lock lock = ...; if (lock.tryLock()) { try { // manipulate protected state } finally { lock.unlock(); } } else { // perform alternative actions }12345678910
boolean tryLock(long time,TimeUnit unit)方法
若是锁在给定的等待时间内空闲,而且当前线程未被中断,则获取锁。若是锁可用,则此方法将当即返回值 true。若是锁不可用,出于线程调度目的,将禁用当前线程,而且在发生如下三种状况之一前,该线程将一直处于休眠状态:
锁由当前线程得到
其余某个线程中断当前线程,而且支持对锁获取的中断;
或已超过指定的等待时间
若是得到了锁,则返回值 true。
若是当前线程:在进入此方法时已经设置了该线程的中断状态;或者
在获取锁时被中断,而且支持对锁获取的中断,则将抛出 InterruptedException,并会清除当前线程的已中断状态。若是超过了指定的等待时间,则将返回值 false。若是 time 小于等于 0,该方法将彻底不等待。
void lockInterruptibly()方法
lockInterruptibly()方法比较特殊,当经过这个方法去获取锁时,若是线程正在等待获取锁,则这个线程可以响应中断,即中断线程的等待状态。也就使说,当两个线程同时经过lock.lockInterruptibly()想获取某个锁时,倘若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法可以中断线程B的等待过程。
因为lockInterruptibly()的声明中抛出了异常,因此lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出InterruptedException。
public void method() throws InterruptedException { lock.lockInterruptibly(); try { doSomeThing(); } finally { lock.unlock(); } }123456789
所以当经过lockInterruptibly()方法获取某个锁时,若是不能获取到,只有进行等待的状况下,是能够响应中断的。而用synchronized修饰的话,当一个线程处于等待某个锁的状态,是没法被中断的,只有一直等待下去。
Condition newCondition()
返回绑定到此 Lock 实例的新 Condition 实例。
在等待条件前,锁必须由当前线程保持。调用 Condition.await() 将在等待前以原子方式释放锁,并在等待返回前从新获取锁。
3:ReentrantLock简介
基本概念:
一个可重入的互斥锁 Lock,它具备与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
ReentrantLock 将由最近成功得到锁,而且尚未释放该锁的线程所拥有。当锁没有被另外一个线程所拥有时,调用 lock 的线程将成功获取该锁并返回。若是当前线程已经拥有该锁,此方法将当即返回。可使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此状况是否发生。
此类的构造方法接受一个可选的公平 参数。当设置为 true 时,在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程。不然此锁将没法保证任何特定访问顺序。
使用公平锁的众多线程中的一员可能得到多倍的成功机会,这种状况发生在其余活动线程没有被处理而且目前并未持有锁时。还要注意的是,未定时的 tryLock 方法并无使用公平设置。由于即便其余线程正在等待,只要该锁是可用的,此方法就能够得到成功。
package com.csu.thread; import java.util.HashSet; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class ReenteantLockTest { //static Lock lock=new ReentrantLock(); private static HashSet<Integer> hashSet=new HashSet<>(); public static void main(String[] args) { // TODO Auto-generated method stub Sum sum=new Sum(); new Thread(new Runnable() { @SuppressWarnings("static-access") public void run() { sum.add(10000); try { Thread.currentThread().sleep(10000); System.out.println("当前线程:"+Thread.currentThread().getName()+"结束"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }).start(); new Thread(new Runnable() { public void run() { sum.add(20000); System.out.println("当前线程:"+Thread.currentThread().getName()+"结束"); } }).start(); } public static class Sum { private Lock lock=new ReentrantLock(); public void add(int i) { lock.lock(); int sum=0; try { System.out.println("当前线程:"+Thread.currentThread().getName()+"获得了锁"); for(int j=0;j<i;j++) { sum+=j; } } catch (Exception e) { // TODO: handle exception } finally { lock.unlock(); System.out.println("当前线程:"+Thread.currentThread().getName()+"释放锁"); } hashSet.add(sum); } } }1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
运行结果:
当前线程:Thread-0获得了锁 当前线程:Thread-0释放锁 当前线程:Thread-1获得了锁 当前线程:Thread-1释放锁 当前线程:Thread-1结束 当前线程:Thread-0结束123456
咱们发现Thread-0没有结束,就及时的释放了线程获取的资源。
tryLock():
import java.util.HashSet; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class ReenteantLockTest { //static Lock lock=new ReentrantLock(); private static HashSet<Integer> hashSet=new HashSet<>(); public static void main(String[] args) { // TODO Auto-generated method stub Sum sum=new Sum(); new Thread(new Runnable() { public void run() { sum.add(1000000000); System.out.println("当前线程:"+Thread.currentThread().getName()+"结束"); } }).start(); new Thread(new Runnable() { public void run() { sum.add(20000); System.out.println("当前线程:"+Thread.currentThread().getName()+"结束"); } }).start(); } public static class Sum { private Lock lock=new ReentrantLock(); public void add(int i) { if(lock.tryLock()){ int sum=0; try { System.out.println("当前线程:"+Thread.currentThread().getName()+"获得了锁"); for(int j=0;j<i;j++) { sum+=j; } } catch (Exception e) { // TODO: handle exception } finally { lock.unlock(); System.out.println("当前线程:"+Thread.currentThread().getName()+"释放锁"); } hashSet.add(sum); } else { System.out.println("当前线程:"+Thread.currentThread().getName()+"获取锁失败"); } } } }12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
运行结果:
当前线程:Thread-0获得了锁 当前线程:Thread-1获取锁失败 当前线程:Thread-1结束 当前线程:Thread-0释放锁 当前线程:Thread-0结束12345
发现:Thread-1获取锁失败,可是它不阻塞,从结果咱们能够发现:当前线程:Thread-1结束 打印出来就表示线程没有阻塞。
lockInterruptibly()
package com.csu.thread; import java.util.HashSet; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class ReenteantLockTest { //static Lock lock=new ReentrantLock(); private static HashSet<Integer> hashSet=new HashSet<>(); public static void main(String[] args) { // TODO Auto-generated method stub Sum sum=new Sum(); Thread t1=new Thread(new Runnable() { public void run() { try { sum.add(1000000000); System.out.println("当前线程:"+Thread.currentThread().getName()+"结束"); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()+"被中断"); } } }); Thread t2=new Thread(new Runnable() { public void run() { try { sum.add(20000); System.out.println("当前线程:"+Thread.currentThread().getName()+"结束"); } catch (InterruptedException e) { // TODO Auto-generated catch block System.out.println(Thread.currentThread().getName()+"被中断"); } } }); t1.start(); t2.start(); t2.interrupt(); } public static class Sum { private Lock lock=new ReentrantLock(); @SuppressWarnings("static-access") public void add(int i) throws InterruptedException { lock.lockInterruptibly(); int sum=0; try { System.out.println("当前线程:"+Thread.currentThread().getName()+"获得了锁"); for(int j=0;j<i;j++) { sum+=j; } Thread.currentThread().sleep(10000); } catch (Exception e) { // TODO: handle exception } finally { lock.unlock(); System.out.println("当前线程:"+Thread.currentThread().getName()+"释放锁"); } hashSet.add(sum); } } }1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
运行结果:
当前线程:Thread-0获得了锁Thread-1被中断 当前线程:Thread-0释放锁 当前线程:Thread-0结束1234
是的Thread-1被成功的中断了。
当咱们注释掉这段代码运行发现:t2.interrupt();
当前线程:Thread-0获得了锁 当前线程:Thread-0释放锁 当前线程:Thread-1获得了锁 当前线程:Thread-0结束 当前线程:Thread-1释放锁 当前线程:Thread-1结束123456
经过结果发现,若是咱们不中断等待线程,则lockInterruptibly是阻塞的,它会等待获取锁。
4:ReadWriteLock
ReadWriteLock 维护了一对相关的锁,一个用于只读操做,另外一个用于写入操做。只要没有 writer,读取锁能够由多个 reader 线程同时保持。写入锁是独占的。
全部 ReadWriteLock 实现都必须保证 writeLock 操做的内存同步效果也要保持与相关 readLock 的联系。也就是说,成功获取读锁的线程会看到写入锁以前版本所作的全部更新。
读-写锁容许对共享数据进行更高级别的并发访问。
尽管读-写锁的基本操做是直截了当的,但实现仍然必须做出许多决策,这些决策可能会影响给定应用程序中读-写锁的效果。这些策略的例子包括:
1:在 writer 释放写入锁时,reader 和 writer 都处于等待状态,在这时要肯定是授予读取锁仍是授予写入锁。Writer 优先比较广泛,由于预期写入所需的时间较短而且不那么频。
2:在 reader 处于活动状态而 writer 处于等待状态时,肯定是否向请求读取锁的 reader 授予读取锁。Reader 优先会无限期地延迟 writer,而 writer 优先会减小可能的并发。
3:肯定是否从新进入锁:可使用带有写入锁的线程从新获取它吗?能够在保持写入锁的同时获取读取锁吗?能够从新进入写入锁自己吗?
4:能够将写入锁在不容许其余 writer 干涉的状况降低级为读取锁吗?能够优先于其余等待的 reader 或 writer 将读取锁升级为写入锁吗?
ReadWriteLock也是一个接口,在它里面只定义了两个方法:
public interface ReadWriteLock { /** * Returns the lock used for reading. * * @return the lock used for reading. */ Lock readLock(); /** * Returns the lock used for writing. * * @return the lock used for writing. */ Lock writeLock(); }123456789101112131415
一个用来获取读锁,一个用来获取写锁。也就是说将文件的读写操做分开,分红2个锁来分配给线程,从而使得多个线程能够同时进行读操做。下面的ReentrantReadWriteLock实现了ReadWriteLock接口。
5:ReentrantReadWriteLock
此类不会将读取者优先或写入者优先强加给锁访问的排序。可是,它确实支持可选的公平 策略。
此锁容许 reader 和 writer 按照 ReentrantLock 的样式从新获取读取锁或写入锁。在写入线程保持的全部写入锁都已经释放后,才容许重入 reader 使用它们。
writer 能够获取读取锁,但反过来则不成立。
重入还容许从写入锁降级为读取锁,其实现方式是:先获取写入锁,而后获取读取锁,最后释放写入锁。可是,从读取锁升级到写入锁是不可能的。
读取锁和写入锁都支持锁获取期间的中断。
写入锁提供了一个 Condition 实现,对于写入锁来讲,该实现的行为与 ReentrantLock.newCondition() 提供的 Condition 实现对 ReentrantLock 所作的行为相同。固然,此 Condition 只能用于写入锁。
读取锁不支持 Condition,readLock().newCondition() 会抛出 UnsupportedOperationException。
实例1:下面的代码展现了如何利用重入来执行升级缓存后的锁降级
class CachedData { Object data; volatile boolean cacheValid; ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); void processCachedData() { rwl.readLock().lock(); if (!cacheValid) { // Must release read lock before acquiring write lock rwl.readLock().unlock(); rwl.writeLock().lock(); // Recheck state because another thread might have acquired // write lock and changed state before we did. if (!cacheValid) { data = ... cacheValid = true; } // Downgrade by acquiring read lock before releasing write lock rwl.readLock().lock(); rwl.writeLock().unlock(); // Unlock write, still hold read } use(data); rwl.readLock().unlock(); } }1234567891011121314151617181920212223242526
实例二:在使用某些种类的 Collection 时,可使用 ReentrantReadWriteLock 来提升并发性。
class RWDictionary { private final Map<String, Data> m = new TreeMap<String, Data>(); private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); private final Lock r = rwl.readLock(); private final Lock w = rwl.writeLock(); public Data get(String key) { r.lock(); try { return m.get(key); } finally { r.unlock(); } } public String[] allKeys() { r.lock(); try { return m.keySet().toArray(); } finally { r.unlock(); } } public Data put(String key, Data value) { w.lock(); try { return m.put(key, value); } finally { w.unlock(); } } public void clear() { w.lock(); try { m.clear(); } finally { w.unlock(); } } }123456789101112131415161718192021222324252627
一般,在预期 collection 很大,读取者线程访问它的次数多于写入者线程,而且 entail 操做的开销高于同步开销时,这很值得一试。
实例三:大量读取锁
1:用同步关键字
public class ReadWriterLockTest { public static void main(String[] args) { final ReadWriterLockTest test = new ReadWriterLockTest(); new Thread(){ public void run() { test.get(Thread.currentThread()); }; }.start(); new Thread(){ public void run() { test.get(Thread.currentThread()); }; }.start(); } public synchronized void get(Thread thread) { long start = System.currentTimeMillis(); while(System.currentTimeMillis() - start <= 1) { System.out.println(thread.getName()+"正在进行读操做"); } System.out.println(thread.getName()+"读操做完毕"); } }1234567891011121314151617181920212223242526272829
Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0读操做完毕Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1读操做完毕123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
咱们发现,直到thread1执行完读操做以后,才会打印thread2执行读操做的信息。
2:改成ReentrantReadWriteLock
package com.csu.thread; import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriterLockTest { private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); public static void main(String[] args) { final ReadWriterLockTest test = new ReadWriterLockTest(); new Thread(){ public void run() { test.get(Thread.currentThread()); }; }.start(); new Thread(){ public void run() { test.get(Thread.currentThread()); }; }.start(); } public void get(Thread thread) { rwl.readLock().lock(); try { long start = System.currentTimeMillis(); while(System.currentTimeMillis() - start <= 1) { System.out.println(thread.getName()+"正在进行读操做"); } System.out.println(thread.getName()+"读操做完毕"); } catch (Exception e) { // TODO: handle exception }finally { rwl.readLock().unlock(); } } }12345678910111213141516171819202122232425262728293031323334353637383940414243
运行结果:
Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-0正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-0正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-0读操做完毕Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1正在进行读操做Thread-1读操做完毕12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
说明thread1和thread2在同时进行读操做。这样就大大提高了读操做的效率。不过要注意的是,若是有一个线程已经占用了读锁,则此时其余线程若是要申请写锁,则申请写锁的线程会一直等待释放读锁。
若是有一个线程已经占用了写锁,则此时其余线程若是申请写锁或者读锁,则申请的线程会一直等待释放写锁。
6.Lock和synchronized的选择
总结来讲,Lock和synchronized有如下几点不一样:
1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
2)synchronized在发生异常时,会自动释放线程占有的锁,所以不会致使死锁现象发生;而Lock在发生异常时,若是没有主动经过unLock()去释放锁,则极可能形成死锁现象,所以使用Lock时须要在finally块中释放锁;
3)Lock可让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不可以响应中断;
4)经过Lock能够知道有没有成功获取锁,而synchronized却没法办到。
5)Lock能够提升多个线程进行读操做的效率。
在性能上来讲,若是竞争资源不激烈,二者的性能是差很少的,而当竞争资源很是激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。因此说,在具体使用时要根据适当状况选择。
7:锁的相关概念介绍
1.可重入锁
若是锁具有可重入性,则称做为可重入锁。像synchronized和ReentrantLock都是可重入锁,可重入性在我看来实际上代表了锁的分配机制:基于线程的分配,而不是基于方法调用的分配。举个简单的例子,当一个线程执行到某个synchronized方法时,好比说method1,而在method1中会调用另一个synchronized方法method2,此时线程没必要从新去申请锁,而是能够直接执行方法method2。
class MyClass { public synchronized void method1() { method2(); } public synchronized void method2() { } }123456789
上述代码中的两个方法method1和method2都用synchronized修饰了,假如某一时刻,线程A执行到了method1,此时线程A获取了这个对象的锁,而因为method2也是synchronized方法,假如synchronized不具有可重入性,此时线程A须要从新申请锁。可是这就会形成一个问题,由于线程A已经持有了该对象的锁,而又在申请获取该对象的锁,这样就会线程A一直等待永远不会获取到的锁。
而因为synchronized和Lock都具有可重入性,因此不会发生上述现象。
2.可中断锁
可中断锁:顾名思义,就是能够相应中断的锁。
在Java中,synchronized就不是可中断锁,而Lock是可中断锁。
若是某一线程A正在执行锁中的代码,另外一线程B正在等待获取该锁,可能因为等待时间过长,线程B不想等待了,想先处理其余事情,咱们可让它中断本身或者在别的线程中中断它,这种就是可中断锁。
在前面演示lockInterruptibly()的用法时已经体现了Lock的可中断性。
3.公平锁
公平锁即尽可能以请求锁的顺序来获取锁。好比同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最早请求的线程)会得到该所,这种就是公平锁。
非公平锁即没法保证锁的获取是按照请求锁的顺序进行的。这样就可能致使某个或者一些线程永远获取不到锁。
在Java中,synchronized就是非公平锁,它没法保证等待的线程获取锁的顺序。
而对于ReentrantLock和ReentrantReadWriteLock,它默认状况下是非公平锁,可是能够设置为公平锁。
4.读写锁
读写锁将对一个资源(好比文件)的访问分红了2个锁,一个读锁和一个写锁。
正由于有了读写锁,才使得多个线程之间的读操做不会发生冲突。
ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。
能够经过readLock()获取读锁,经过writeLock()获取写锁。
上面已经演示过了读写锁的使用方法,在此再也不赘述。
引用块内容
http://www.cnblogs.com/dolphin0520/p/3923167.html java核心技术 卷I