java 使用lock实现同步

同步代码块的另一种机制。

 

一、是什么

lock是Java提供的同步代码块的另一种机制,它是一种比synchronized关键字更强大也更灵活的机制。这种机制基于Lock接口及其实现类(例如ReentrantLock),提供了更多的好处。

二、为什么使用lock

1.lock支持更灵活的同步代码块结构

使用synchronized关键字时,只能在同一个synchronized块结构中获取和释放控制。Lock接口允许实现更复杂的临界区结构(即控制的获取和释放不出现在同一个块结构中)。

2.lock功能更多

相比synchronized关键字,Lock接口提供了更多的功能。其中一个新的功能是tryLock()方法的实现。这个方法试图获取锁,如果锁已经被其他线程获取,它将返回false并继续往下执行代码。

使用synchronized关键字时,如果线程A试图执行一个同步代码块,而线程B已在执行这个同步代码块,则线程A就会被挂起直到线程B运行完这个同步代码块。使用锁的tryLock()方法,通过返回值将得知是否有其他线程正在使用这个锁保护的代码块。

3.lock性能更好

Lock接口允许分离读和写操作,允许多个读线程和只有一个写线程。相比synchronized关键字,Lock接口具有更好的性能。

三、如何使用

1.简单使用

先写一个简单的示例:

输出如下:

可以发现,lock.lock()到lock.unlock()之间的代码(共享资源)变成了同步资源。在thread1释放这个lock前,thread2被阻塞,只能等待thread1结束了对同步资源的执行后,才能执行。

实现的效果类似synchronized锁:

这里需要注意的是,用sychronized修饰的方法或者语句块在代码执行完之后锁自动释放,而用Lock需要我们手动释放锁,所以为了保证锁最终被释放(发生异常情况),要把互斥区(就是竞争的部分)放在try内,释放锁放在finally内。这里一定要记得手动释放锁,不然就变成死锁了。

我的个人理解:

这里体现了lock更灵活的特点。lock.lock()和lock.unLock()可以在代码的try catch逻辑中加锁,释放锁,粒度比较小。而sychronized普遍修饰一个代码块(类/方法/块),需要仔细考虑代码块中包含哪些逻辑,否则将影响性能。

2.tryLock方法

上面我们提到tryLock方法,这个方法具体怎么使用呢?

结果为:

lock.tryLock()仅在调用时锁为空闲状态才获取该锁。如果锁可用,则获取锁,并立即返回true。如果锁不可用,则此方法将立即返回false。

也就是说,lock.tryLock()可以写在if else语句中,如果lock.tryLock()能成功获取锁(此时已经获取了锁,相当于进行了一次lock.lock()操作),就需要在try catch finally中使用lock.unLock()手动释放锁。如果没有获取锁,就不需要手动释放了,可以做点别的操作。

没有获得锁的时候,不要进行手动释放,否则将会报出java.lang.IllegalMonitorStateException错误。

我的个人理解:

这里体现了lock功能更多的特点。sychronized是自己加锁自己释放,难以控制。而lock则提供了更多的方法,来人为管理锁。

3.读写锁

这里很能体现lock的性能优势。

在对数据进行读写的时候,为了保证数据的一致性和完整性,读和写是互斥的,写和写是互斥的,但是读和读是不需要互斥的,所以加锁的时候应该考虑到以上几种不同情景,加以区分。

如果使用sychronized,很难做到这一点。但lock却可以依靠读写锁来实现。下篇文章再详细实验。

四、总结

lock给java提供了更多的可能性。

发表评论

电子邮件地址不会被公开。 必填项已用*标注