Overview

  • 介绍java的lock interface.

Motivation

  • java拥有像synchronized这样的内置锁,那为什么还需要lock这样的外置锁呢?
  • 首先,性能不是选择synchronized或lock的原因,因为jdk6中synchronized的性能已经和lock相差不大。
  • 一般,选择lock是基于lock拥有的以下几个优点(内置锁不具备):
    • 当获取锁时可以有一个等待时间,不会无期限等待下去;
    • 当获取不到锁时,能够响应中断;[lockInterruptibly()]
    • 可以在多读少写的应用场景中,提高性能;  [new Condition()]
    • 可以在获取不到锁时,立即返回false,获取到锁时返回true。[tryLock()]

Lock Interface

  • public interface Lock {
    void lock(); // acquires the lock, 如果锁被其他线程获取,则进行等待 boolean tryLock(); // 尝试获取锁,成功则返回true,否则返回false void lockInterruptibly() throws InterruptedException; // 通过该方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。 boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition();
    }
  • 其中,
    • lockInterruptibly()表面加锁时,当前拥有锁的线程可以被中断;
    • tryLock()则用于尝试获取锁,能获取则返回true,否则返回false;
    • tryLock(long time, TimeUnit unit)与tryLock类似,只是会尝试一段时间;
    • unlock()用于拥有锁的线程释放锁。
    • newCondition()返回一个新的与当前实例绑定的Condition。

Continue that basic example

  • 对于我们在上面讲的那个经典的生产者消费者的模型。我们已经知道为了防止deadlock,我们用notifyAll()取代了notify()。也就是每次会唤醒所有的存取线程,因为synchronized这种内置锁是加在类实例之上的,put()和take()共用一把锁。
  • 很明显,为了优化这种方式,我们想每次唤醒的是状态能够发生变化的线程。比如说我put()之后就只想唤醒消费者线程,因为此时唤醒生产者线程是无意义的。
  • 这种想法很明显只有通过将两种进程放到不同的等待队列,才能实现。
  • 这就要用到上述Lock接口的newCondition()方法。
  • 基于Lock interface的生产者消费者模型部分代码如下:
  • private final Lock lock = new ReentrantLock();
    
    private final Condition notFullCondition = lock.newCondition();
    private final Condition notEmptyCondition = lock.newCondition(); public void put(Object obj) throws InterruptedException{
    lock.lock();
    try {
    while (count == DEFAULT_BUFFER_SIZE) {
    System.out.println("the buffer is full,wait for a moment for putting ["+obj+"] to the buffer"+",thread:"+Thread.currentThread().getId());
    notFullCondition.await(); // wait for the notFullCondition
    }
    if (tail >= DEFAULT_BUFFER_SIZE) tail = 0; buffer[tail] = obj;
    count++;
    System.out.println("success put the data ["+obj+"] to buffer,thread:"+Thread.currentThread().getId()); // then we invoke the thread in the notEmptyCondition wait queue
    notEmptyCondition.signal();
    } finally{
    lock.unlock();
    }
    } public Object take() throws InterruptedException {
    lock.lock();
    Object res;
    try {
    while (count == EMPTY) {
    System.out.println("the buffer is empty,just wait a moment,thread:"+Thread.currentThread().getId());
    notEmptyCondition.await(); // wait for notEmptyCondition
    }
    res = buffer[header];
    if (++header >= DEFAULT_BUFFER_SIZE) header = 0; count--;
    if (count < EMPTY) count = 0; notFullCondition.signal(); // invoke the threads in the notFullCondition wait queue
    } finally {
    lock.unlock();
    }
    return res;
    }
  • 上述代码中要注意:Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。所以一般来说,使用Lock必须在try catch块中进行,并且在finally块中释放锁,以保证锁一定被释放,防止deadlock的发生。
  • ReentrantLock表示可重入锁,ReentrantLock是唯一实现了的Lock接口类。

ReentrantLock VS synchronized

Synchronized

  • 把代码声明为synchronized,可以同时拥有原子性和可见性:

    • 原子性:在同一时刻只有一个线程能对该段代码进行操作,从而防止在多个线程更新共享状态时互相冲突;
    • 可见性:可见性需要对付内存缓存和编译器优化的各种反常行为(在多核处理器中,若多个线程对一个变量进行操作,这多个线程有可能被分配到多个处理器中运行,那么编译器会对代码进行优化:将变量复制一份到自己的片上存储器中,操作完后才赋值回主存,以此来提高速度。同样的,在单核处理器上也存在着由于“备份”造成的问题。)。一般来说,线程以某种不必让其他线程立即可以看到的方式。

ReentrantLock类

  • 首先,ReentrantLock是一个类,implement Lock接口。通过使用类,而不是作为语言的特性来实现,这就为Lock的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。
  • ReentrantLock在功能性方面更全面,比如时间锁等候,可中断锁等候,锁投票等。

最新文章

  1. EF架构~通过EF6的DbCommand拦截器来实现数据库读写分离
  2. Python 简介和入门
  3. 如何用openvr api打开vive前置摄像头
  4. Two classes have the same XML type name 排错【转】
  5. SQLServer的数据类型
  6. 批量硬关联本地AD帐号与Office云端帐号
  7. MySQL 参数autoReconnect=true 解决8小时连接失效
  8. sql操作table
  9. Oracle调优总结(经典实践 重要)
  10. JavaScript 数据类型转换(显式与隐式)
  11. Javascript 之内置对象
  12. LeetCode OJ 230. Kth Smallest Element in a BST
  13. LeetCode 871 - 最低加油次数 - [贪心+优先队列]
  14. LeetCode(79): 单词搜索
  15. java进阶书籍推荐(不包括基础)
  16. 3ds max学习笔记(十三)-- (锥化,扭曲,晶格)
  17. Java 模仿 C# 字典 一例
  18. 自学工业控制网络之路1.4-典型的现场总线介绍CAN
  19. 11:django 模板 内建标签
  20. MBR详解

热门文章

  1. 小程序授权demo
  2. 【洛谷p2822】组合数问题
  3. linux下网络查看命令ss
  4. python记录_day23 正则表达式 re模块
  5. 4月24 php基础及函数的应用
  6. 自用chrome+油猴脚本,使用迅雷下载百度云大文件,一键离线下载
  7. 八、持久层框架(MyBatis)
  8. Docker的简单介绍及使用
  9. 解决iOS第三方SDK之间重复的symbols问题
  10. [LeetCode] 108. Convert Sorted Array to Binary Search Tree ☆(升序数组转换成一个平衡二叉树)