同步锁
synchronized同步代码块和同步方法使用一种封闭式的锁机制,使用起来非常简单,也能够解决线程同步过程中出现的线程安全问题,但也有一些限制,例如它无法中断一个正在等候获得锁的线程,也无法通过轮询得到锁,如果不想等下去,也就没法得到锁。
从JDK 5开始,Java增加了一个功能更强大的Lock锁。Lock锁与synchronized隐式锁在功能上基本相同,其最大的优势在于Lock锁可以让某个线程在持续获取同步锁失败后返回,不再继续等待,另外Lock锁在使用时也更加灵活。
接下来将售票案例改为使用Lock锁进行演示,如文件1所示。
文件1 Example14.java
1 import java.util.concurrent.locks.*;
2 // 定义LockThread类实现Runnable接口
3 class LockThread implements Runnable {
4 private int tickets = 10; // 10张票
5 // 定义一个Lock锁对象
6 private final Lock lock = new ReentrantLock();
7 public void run() {
8 while (true) {
9 lock.lock(); // 对代码块进行加锁
10 if (tickets > 0) {
11 try {
12 Thread.sleep(100); // 模拟售票耗时过程
13 System.out.println(Thread.currentThread().getName()
14 + " 正在发售第 " + tickets-- + " 张票 ");
15 } catch (InterruptedException e) {
16 e.printStackTrace();
17 }finally{
18 lock.unlock(); // 执行完代码块后释放锁
19 }
20 }
21 }
22 }
23 }
24 public class Example14 {
25 public static void main(String[] args) {
26 LockThread lockThread = new LockThread();
27 // 创建并开启四个线程,模拟4个售票窗口
28 new Thread(lockThread, "窗口1").start();
29 new Thread(lockThread, "窗口2").start();
30 new Thread(lockThread, "窗口3").start();
31 new Thread(lockThread, "窗口4").start();
32 }
33 }
运行结果如图1所示。
图1 运行结果
文件1中,通过Lock接口的实现类ReentrantLock来创建一个Lock锁对象,并通过Lock锁对象的lock()方法和unlock()方法对核心代码块进行了上锁和解锁。从图1可以看出,使用Lock同步锁也可以实现正常售票,解决线程同步过程中的安全问题。
需要注意的是,ReentrantLock类是Lock锁接口的实现类,也是常用的同步锁,在该同步锁中除了lock()方法和unlock()方法外,还提供了一些其他同步锁操作的方法,例如tryLock()方法可以判断某个线程锁是否可用。另外,在使用Lock同步锁时,可以根据需要在不同代码位置灵活的上锁和解锁,为了保证所有情况下都能正常解锁以确保其他线程可以执行,通常情况下会在finally{}代码块中调用unlock()方法来解锁。