Lock实现提供了比使用synchronized方法或者语句更有扩展性的锁定操作,有更多的属性,可以关联多个Condition。
锁是一种用于控制多个线程对共享资源的访问的工具。通常,锁提供对共享资源的独占访问,并且对共享资源的访问都需要先获得锁。但有的锁支持并发访问共享资源,如ReadWriteLock的读锁。
synchronized方法或语句可以访问每个对象关联的隐式监视器锁,但获取和释放都必须以此块为单位,当获取多个锁时,必须在获取他们的此法范围内以相反的顺序释放。
synchronized使得编程更加容易,但有时候需要更加灵活的使用方式,比如获取结点A的锁,然后获取结点B的锁,然后释放A并且获取C,然后释放B并且获取D,Lock接口的可以实现,允许在不同的范围内获取和释放锁,并允许以任何顺序获取和释放多个锁。但同时,synchronized是自己获取和释放锁,lock要求手动lock和unlock。
当锁定和解锁发生在不同的范围内时,必须注意确保在保持锁定时执行的所有代码都受try-finally或try-catch保护,以确保在必要时释放锁。
被synchronized修饰的程序块经过编译后,会在前后生成monitorenter和monitorexit两个字节码指令。为了实现互斥锁,JVM的monitorenter和monitorexit字节码依赖底层操作系统的互斥锁来实现,java层面的线程与操作系统的原生线程有映射关系,这时如果要将一个线程进行阻塞或唤起都需要操作系统的协助,这就需要从用户态切换到内核态来执行,这种切换代价十分昂贵,需要消耗很多处理器时间。如果可能,应该减少这样的切换。
Synchronized还有另外一个重要的特性——可重入性。
synchronized实现的是一个非公平锁,非公平主要表现在获取锁的行为上,并非是按照申请锁的时间前后给等待线程分配锁的,每当锁被释放后,任何一个线程都有机会竞争到锁,这样做的目的是为了提高执行性能,当然也会产生线程饥饿现象。
synchronized最后一个特性(缺点)就是不可中断性,在所有等待的线程中,你们唯一能做的就是等,而实际情况可能是有些任务等了足够久了,我要取消此任务去干别的事情,此时synchronized是无法帮你实现的,它把所有实现机制都交给了JVM,提供了方便的同时也体现出了自己的局限性。