多线程之重入锁ReentrantLock(四)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jiuqiyuliang/article/details/70174743

在博文多线程之内存可见性Volatile(一)多线程之原子变量CAS算法(二)中,我介绍了如何安全的访问共享对象,给了两种解决方案,java5.0之后,增加了lock接口的高级功能。

这篇博文,我们介绍lock接口的一种实现,重入锁ReentrantLock,我们只是简单的介绍和synchronized进行一下对比。对于重入锁更加深入的内容,后面我们会再详细的写。

ReentrantLock

ReentrantLock是一个可重入的互斥锁,重入锁是一种递归无阻塞的同步机制。即ReentrantLock实现了Lock接口,并提供了与synchronized相同的互斥性和内存可见性。但相较于synchronized提供了更高的处理锁的灵活性。

可重入概念

若一个程序或子程序可以“安全的被并行执行(Parallel computing)”,则称其为可重入(reentrant或re-entrant)的。即当该子程序正在运行时,可以再次进入并执行它(并行执行时,个别的执行结果,都符合设计时的预期)。可重入概念是在单线程操作系统的时代提出的。

重入锁的构造方法提供了一个可选的公平参数:

不公平锁与公平锁的区别:

  1. 公平情况下,操作会排一个队按顺序执行,来保证执行顺序。(会消耗更多的时间来排队)
  2. 不公平情况下,是无序状态允许插队,jvm会自动计算如何处理更快速来调度插队。(如果不关心顺序,这个速度会更快)
 public ReentrantLock() {
        sync = new NonfairSync();
    }
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

ReentrantLock与synchronized的比较

相同:ReentrantLock提供了synchronized类似的功能和内存语义。
不同:
(1)ReentrantLock功能性方面更全面,比如时间锁等候,可中断锁等候,锁投票等,因此更有扩展性。在多个条件变量和高度竞争锁的地方,用ReentrantLock更合适,ReentrantLock还提供了Condition,对线程的等待和唤醒等操作更加灵活,一个ReentrantLock可以有多个Condition实例,所以更有扩展性。
(2)ReentrantLock 的性能比synchronized会好点。
(3)ReentrantLock提供了可轮询的锁请求,他可以尝试的去取得锁,如果取得成功则继续处理,取得不成功,可以等下次运行的时候处理,所以不容易产生死锁,而synchronized则一旦进入锁请求要么成功,要么一直阻塞,所以更容易产生死锁。

实现多个客户端同时售票功能

我们使用三个线程,同时售卖20张车票,代码如下:

public class TestLock {

    public static void main(String[] args) {

        Ticket ticket = new Ticket();
        new Thread(ticket,"1号窗口").start();
        new Thread(ticket,"2号窗口").start();
        new Thread(ticket,"3号窗口").start();
    }
}

class Ticket implements Runnable{

    private int tick = 20;

    private Lock lock = new ReentrantLock();

    @Override
    public void run() {

        while(true){
            lock.lock();//如果被其它资源锁定,会在此等待锁释放,达到暂停的效果  
            if(tick > 0){
                try {
                    try {
                        Thread.sleep(200);
                    } catch (Exception e) {
                    }
                    System.out.println(Thread.currentThread().getName() + "完成售票,余票为:" + --tick);
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

注意:
(1) lock 必须在 finally 块中释放。否则,如果受保护的代码将抛出异常,锁就有可能永远得不到释放!这一点区别看起来可能没什么,但是实际上,它极为重要。忘记在 finally 块中释放锁,可能会在程序中留下一个定时炸弹,当有一天炸弹爆炸时,您要花费很大力气才有找到源头在哪。而使用同步,JVM 将确保锁会获得自动释放

总结

重入锁,还有很多的特性,还需要我们更加深入的学习,继续加油吧。

猜你喜欢

转载自blog.csdn.net/jiuqiyuliang/article/details/70174743