在先前的文章中我们讲解过关键字synchronized是用于实现代码块的多线程同步,相信现在大家都知道它是用来控制多线程访问共享资源时的一种方式,能防止多线程访问共享资源而出现线程安全问题。Synchronized也是在jdk1.5之前最常见也是唯一的锁的形式。synchronized是Java的关键字,也就是说它是Java语言内置的特性,它是以面向过程的形式实现同步方法或者代码块,代码略显复杂。从jdk1.6开始新增了Lock接口(以及对应实现类)以更加灵活的面向对象思维来解决多线程同步代码块锁的方法。
Lock方法
Lock接口提供了与synchronized关键字相同的同步功能,能够防止多个线程同时访问共享资源而出现线程安全问题。但不同的是Lock是以接口与实现类的形式完成,它需要在使用同步代码块时手动获取锁和释放锁。虽然没有synchronized关键字自动获取和释放锁那么便捷,但Lock接口却具有了对锁的可操作性,可随时中断获取以及超时获取锁,带来了更加实用的同步特性。Lock接口主要通过ReentrantLock实现类中的lock方法与unlock来帮助我们实现对锁的获取与释放,也就是说我们首先要创建Lock接口与对应的实现类ReentrantLock,调用对应的lock()方法实现获取锁,通过unlock()来实现释放锁。具体语法如下:
//1. 创建一个实现了Lock接口的实现类ReentrantLock的实例对象lock
Lock lock = new ReentrantLock();
//2.在可能会出现安全问题的代码前调用实例对象lock的中的方法lock获取锁
lock.lock();
//3.在可能会出现安全问题的代码后调用实例对象lock的方法unlock释放锁
lock.unlock();
Lock代码示例
理解完Lock接口的作用与语法后,我们还是通过相同的案例(多线程卖票)来加以说明。我们定义一个成员变量来存储对应的10张票,实现每隔100毫秒卖出一张,直到票数卖完为止。代码下:
通过以上的案例中我们不难发现,和我们先前《浅析线程同步锁》的代码基本保持一致,在线程的run方法中将原先的synchronized关键字替换成Lock接口中的lock方法实现对锁的获取,需要注意的是lock方法为保证锁能够被释放,就必须将 unlock锁释放的方法写在finally模块中。
现在我们在测试类main方法中创建三个子线程对象来模拟三个员工同时售票,具体代码如下:
运行以上代码显示效果如下:
总结
从以上的运行结果中我们不难看出,Lock接口是以面向对象的思维以及更加简洁的形式保证了多线程中访问共享资源时出现的线程安全问题。当我们开发过程中从性能上来考虑(例如线程数量较多的时候),Lock接口的性能要远远优于synchronized关键字。