一、前言
在Java基础–多线程【同步机制】中,我们已经学会了使用synchronized关键字通过加锁来实现线程的同步,但通过使用我们可能会发现,这样的方式对于在哪里加了锁,哪里解开的锁不怎么好理解。
二、Lock接口的基本介绍
通过Lock接口来实现线程同步,能够使程序结构更为灵活。
并且其提供了很多实用的方法,功能更强大、性能更优越。
其常用的方法如下:
void lock():获取锁,如果锁被占用,则等待
boolean tryLock():尝试获取锁(成功返回true、失败返回false,不阻塞)
void unlock():释放锁
三、Lock的实现类
Lock的实现类还是有很多的(可查看Java的Lock实现类介绍),但是最常用的两个如下:
ReentrantLock:与synchronized关键字一样具有互斥功能
ReentrantReadWriteLock:一种支持一写多读的同步锁
四、重入锁
(一)什么是可重入锁
可重入锁与不可重入锁
接下来让我们通过两个案例来进一步了解重入锁:
(二)案例
案例一
需求:有一共享资源(数组),现希望通过使用重入锁来保证往数组中添加数据是线程安全的。
代码实现:
1 | public class MyList { |
1 | public class TestMyList { |
案例二
需求:利用重入锁来实现买票程序的线程安全
代码实现:
1 | public class Ticket implements Runnable{ |
1 | public class TestTicket { |
五、读写锁
(一)读写锁的基本介绍
ReentrantReadWriteLock:
一种支持一写多读的同步锁,读写分离,可分别分配读锁和写锁;
支持多次分配读锁,使多个读操作可以并发执行。
其互斥规则如下:
写-写:互斥,阻塞读-写:互斥,读阻塞写,写阻塞读
读-读:不互斥,不阻塞
在读操作远高于写操作的环境中,可在保障线程安全的前提下,提高运行效率。
(二)案例
需求:
假设有一数据,对它的读操作多于写操作。
此处设置为,18次读操作,2次写操作,请分别通过普通锁和读写锁来测试完成这些操作的总用时。
1、普通锁方式
1 | public class MyClass01 { |
测试代码:
1 | public class TestMyClass { |
测试结果:
1 | 写入数据:Hello World |
即:总共耗时 20070 ms(不管是读还是写的线程都需要1s执行时间)
2、读写锁方式
1 | public class MyClass { |
测试代码:
和普通锁的方式一样,只不过在第7行中的创建资源时,创建的对象为MyClass02即可。
1 | public class TestMyClass { |
测试结果:
1 | 写入数据:Hello World |
即:总共耗时 3126 ms(2次写操作各占1s,18次读操作共占1s)
六、总结
关于synchronized实现同步的介绍请查看:Java并发编程:Lock
关于Lock和synchronized的区别请查看:Lock和synchronized的区别和使用
本文只是大概总结了Lock的使用,对于Lock的原理请查看:一文带你理解Java中Lock的实现原理
Java新手,若有错误,欢迎指正!