上一篇文章中:线程安全问题经典案例—卖票,我们使用了synchronized
同步代码块来实现了线程安全,这篇文章我们使用Lock
锁来实现同样的效果。
Lock锁介绍 |
Lock
锁是java jdk1.5版本之后添加一个处理线程安全问题的接口。相比较synchronized
而言,更加灵活,因为它不在局限于一个代码块,实现了一些synchronized
同步锁所不能实现的功能。
卖票案例代码实现 |
public class RunnableImpl implements Runnable {
//给一个共享资源
private int ticket = 100;
//new 一个Lock锁的实现类对象
Lock lock = new ReentrantLock();
//指定线程所要执行的任务(卖票)
@Override
public void run() {
while (true) {
//在可能会出现线程安全问题的地方 显式的调用lock中的.lock()方法进行加锁
lock.lock();
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张电影票");
//为了卖票出错的情况更加明显,我们让线程等待100ms
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
--ticket;
}
//在代码执行结束的地方,显式的调用lock对象中的.unlock()方法,释放锁资源
lock.unlock();
}
}
}
public class FileTest {
public static void main(String[] args) {
//思考:为什么这里只new 了一个Runnable的对象传到了三个线程种?
RunnableImpl runnable = new RunnableImpl();
//new 三个线程出来模三个窗口,为了符合情景,我们为线程设置一个线程名
Thread thread1 = new Thread(runnable);
thread1.setName("窗口1");
Thread thread2 = new Thread(runnable);
thread2.setName("窗口2");
Thread thread3 = new Thread(runnable);
thread3.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
启动项目之后,发现直接从100张电影票顺序卖出去了,说明使用Lock锁成功解决了线程安全问题。
synchronized和Lock比较 |
synchronized:
-
优势:
此种方式最大的好处就是不需要显式的去加锁
和释放锁
,线程A执行到同步代码块的地方,自动判断synchronized
是否有对象锁,有则进入同步代码块中执行任务,没有对象锁就说明此刻有其他线程B正在执行此同步代码块,线程A则进入阻塞状态。 -
劣势:
相比较Lock
而言,synchronized
同步锁比较死板,不够灵活,因为synchronized
一般使用在同步代码块或者同步方法上,都可以看作是一个块状结构,也就是强制要求了所有的获取锁和释放锁都必须在一个块结构中。但是Lock
就没有此种困扰。
Lock:
Lock
的优势不再赘述,这里就强调一点:使用Lock
一定要注意不要忘记添加lock.unlock()
显示的释放锁资源,在项目开发的过程中,我们一般将它放到try()catch{……}finnly{lock.unlock()}
的finnly{}代码块中,也就是说,无论try()代码块中有没有捕获到异常,该锁始终会被释放掉。
public class RunnableImpl implements Runnable {
//给一个共享资源
private int ticket = 100;
//new 一个Lock锁的实现类对象
Lock lock = new ReentrantLock();
//指定线程所要执行的任务(卖票)
@Override
public void run() {
while (true) {
lock.lock();
if (ticket > 0) {
try {
System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张电影票");
//为了卖票出错的情况更加明显,我们让线程等待100ms
--ticket;
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
}