在Java5 诞生之前,Java线程同步和协助只能使用synchronized锁机制,这种锁机制由JVM直接提供实现,所以经常又称内部锁,也称作对象监视器,由于这种锁有性能缺陷而且灵活不足,所以牛人在Java5添加了Lock接口及相关实现,为Java多线程同步与协作提供了另一种选择,当然,后来者不管功能还是性能都比前者优秀,以致有人倡导全部使用Lock抛弃synchronized。有了竞争就会迫使进步,果然Java 6对内部锁进行优化,性能大幅度提升,大部分情况下与Lock可以相媲美(但非公平竞争情况下Lock还是存在性能优势)而且JCP专家组提倡简单正常情况下使用synchronized内部锁,并称将来内部锁还会继续进行优化,所以现在synchronized还是广为使用。然而这只是性能方面,在功能方面Lock提供了更多的灵活性,如轮询锁、定时锁、可中断的锁获取、非块结构的加解锁等,并且在Java5以后的并发包中广为使用,所以学习研究Lock还是必须的。
public interface Lock {
void lock();
void unlock();
}对于简单线程同步,Lock使用lock()方法请求并加锁,需要自己使用unlock()方法释放锁,并不需要限定代码范围,如果忘记会造成死锁,这是灵活性带来的负面影响,所以使用时都会增加try{...}finally{lock.unlock()},以保证锁的释放;synchronized在代码块范围外会自动释放锁,一开始就定义好锁定的代码范围,比较死板但可靠,另外synchronized经常用于修饰类成员方法,线程调用这种方法前需要先获得对应的锁,这种使用方式很顺手,简单明了,Lock没有这种语法。
Lock和synchronized对线程同步操作示例:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
private int mCount;
private Lock mLock;
public TestLock(){
this.mCount = 0;
this.mLock = new ReentrantLock();
}
public static void main(String[] args) throws InterruptedException {
TestLock testLock = new TestLock();
for(int i=0; i<2; i++){
new Thread(testLock.new SThread()).start();
}
TimeUnit.SECONDS.sleep(3);
for(int i=0; i<2; i++){
new Thread(testLock.new LThread()).start();
}
}
private void increaseCount() {
mCount++;
System.out.println(Thread.currentThread().getName() + " do lock, count: " + mCount);
}
private void decreaseCount() {
mCount--;
System.out.println(Thread.currentThread().getName() + " do unlock, count: " + mCount);
}
private void doCountWithLSync() {
mLock.lock();
try{
increaseCount();
mLock.lock();//re-enter
try{
increaseCount();
}
finally{
mLock.unlock();
decreaseCount();
}
}
finally{
mLock.unlock();
decreaseCount();
}
}
private void doCountWithSSync() {
synchronized(this){
increaseCount();
synchronized(this){//re-enter
increaseCount();
}
decreaseCount();
}
decreaseCount();
}
private class LThread implements Runnable{
public void run(){
doCountWithLSync();
}
}
private class SThread implements Runnable{
public void run(){
doCountWithSSync();
}
}
}
















