`
milagro
  • 浏览: 13724 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

转 Java锁机制

阅读更多

内置锁

    Java提供了一种内置的锁机制来支持原子性:同步代码块(synchronized 关键字 ),同步代码块包含两部分:一个作为锁的对象的引用,一个作为由这个锁保护的代码块。

synchronized {

    //代码块

}

     每个Java对象都可以用做一个实现同步的锁,这些锁被秒为内置锁(Intrinsic Lock)或监视锁(Monitor Lock),线程进入同步代码块之前会自动获得锁,并且在退出同步代码块时怎释放锁,而且无论是通过正常路径退出锁还是通过抛异常退出都一样,获得内置锁的唯一途径就是进入由这个锁保护的同步代码块或方法。

    内置锁具有可重入性,当某个线程请求一个由其它线程持有的内置锁时,发出请求的线程就会被阻塞,如果某个线程试图获得一个由它自己持有的锁时,那么这个请求就会成功,这就是内置锁的可重入性,“重入”意味着获取锁的操作的粒度是“线程”,而不是“调用”。重入的一种实现方法是为每个锁关联一个获取计数值和一个所有者线程,当计数为0时,这个锁就被认为是没有被任何线程持有,当线程请求一个未被持有的锁时,JVM将记下锁的持有者,并将获取计数加1,如果同一个线程再次获取这个锁时,计数值将递增,当线程退出同步代码块时,计数值会递减。

下面代码中,如果没有锁的可重入性,那么将产生死锁:

Java代码  收藏代码
  1. public class Widget {  
  2.     public synchronized void doSomething() {  
  3.         ...  
  4.     }  
  5.   
  6. }  
  7. public class LoggingWidget extends Widget {  
  8.     public synchronized void doSomething() {  
  9.         System.out.println(toString() + ": calling doSomething");  
  10.         super.doSomething();//若内置锁是不可重入的,则发生死锁  
  11.     }  
  12. }  

 

显示锁

    在JDK5之前,可使用的同步机制只有synchronized 关键字和volative变量,JDK5增加了一种新的锁机制:ReentrantLock, ReentrantLock并不是替代内置锁的方法,而是当内置锁机制不适用时作为一种可选择的高级功能。

    JDK5提供的锁工具类都在java.util.concurrent.locks包下,有Condition、Lock、ReadWriteLock等接口:

Lock : 接口支持那些语义不同(重入、公平等)的锁规则,可以在非阻塞式结构的上下文(包括 hand-over-hand 和锁重排算法)中使用这些规则。主要的实现是 ReentrantLock 。

ReadWriteLock : 读写锁接口以类似方式定义了一些读取者可以共享而写入者独占的锁。此包只提供了一个实现,即 ReentrantReadWriteLock ,因为它适用于大部分的标准用法上下文。但程序员可以创建自己的、适用于非标准要求的实现。

Condition : 接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用 Object.wait 访问的隐式监视器类似,但提供了更强大的功能。需要特别指出的是,单个 Lock 可能与多个 Condition 对象关联。

    在大多数情况下,显示锁都能很好的工作,但有些局限性,如无法中断一个正在等待获取锁的线程,或者无法在请求获取一个锁时无限的等待下去。Lock提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁的方式都是显示的。下面代码给出了Lock接口使用的标准形式,必须在finally中释放锁:

Java代码  收藏代码
  1. Lock lock = new ReentrantLock();  
  2. ...  
  3. lock.lock();//显示加锁  
  4. try{  
  5.     ...  
  6. }finally{  
  7.     //显示释放锁  
  8.     lock.unlock();  
  9. }  

     boolean tryLock()接口实现可定时与可轮询获取锁的实现,与无条件获取锁的模式相比,它具有更完善的错误恢复机制,在内置锁中,死锁是一种严重的问题,恢复程序的唯一方法就是重启应用,而防止死锁的方法就是在构造程序时避免出现不一致的锁顺序。可定时与可轮询提供了另一种方式:避免死锁的发生。下面代码实现了可轮询获取锁,如果不能同时获得两个锁,那么就退回重试:

Java代码  收藏代码
  1. public boolean transferMoney(Account fromAcct, Account toAcct) {  
  2.     while (true) {  
  3.         if (fromAcct.lock.tryLock()) {  
  4.             try {  
  5.                 if (toAcct.lock.tryLock()) {  
  6.                     try {  
  7.                         ...  
  8.                     }  
  9.                     finally {  
  10.                         toAcct.lock.unlock();  
  11.                     }  
  12.                 }  
  13.             }  
  14.             finally {  
  15.                 fromAcct.lock.unlock();  
  16.             }  
  17.         }  
  18.     }  
  19. }  

     在带有时间限制的操作中调用了一个阻塞方法时,它能根据剩余时间来提供一个时限,如果操作不能在指定的时间内给出结果,那么就会使程序提前结束,下面代码演示了试图在Lock保护的共享通信上发一条消息,如果不能在指定时间内完成,代码就会失败,当时的tryLock能够在这种带有时间限制的操作中实现独占的加锁行为:

Java代码  收藏代码
  1. public boolean trySendOnSharedLine(String message, long timeout, TimeUnit unit) {  
  2.     if (!lock.tryLock(timeout, NANOSECONDS)) {  
  3.         return false;  
  4.     }  
  5.     try {  
  6.         return sendOnSharedLine(message);  
  7.     }  
  8.     finally {  
  9.         lock.unlock();  
  10.     }  
  11. }  

 

ReadWriteLock 读写锁 :分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁。

 

获取锁顺序 
此类不会将读取者优先或写入者优先强加给锁访问的排序。但是,它确实支持可选的公平 策略。 
非公平模式(默认) : 当非公平地(默认)构造时,未指定进入读写锁的顺序,受到 reentrancy 约束的限制。连续竞争的非公平锁可能无限期地推迟一个或多个 reader 或 writer 线程,但吞吐量通常要高于公平锁。 
公平模式 : 当公平地构造线程时,线程利用一个近似到达顺序的策略来争夺进入。当释放当前保持的锁时,可以为等待时间最长的单个 writer 线程分配写入锁,如果有一组等待时间大于所有正在等待的 writer 线程 的 reader 线程,将为该组分配写入锁。如果保持写入锁,或者有一个等待的 writer 线程,则试图获得公平读取锁(非重入地)的线程将会阻塞。直到当前最旧的等待 writer 线程已获得并释放了写入锁之后,该线程才会获得读取锁。当然,如果等待 writer 放弃其等待,而保留一个或更多 reader 线程为队列中带有写入锁自由的时间最长的 waiter,则将为那些 reader 分配读取锁。
试图获得公平写入锁的(非重入地)的线程将会阻塞,除非读取锁和写入锁都自由(这意味着没有等待线程)。(注意,非阻塞 ReentrantReadWriteLock.ReadLock.tryLock() 和 ReentrantReadWriteLock.WriteLock.tryLock() 方法不会遵守此公平设置,并将获得锁(如果可能),不考虑等待线程)。 

重入 
此锁允许 reader 和 writer 按照 ReentrantLock 的样式重新获取读取锁或写入锁。在写入线程保持的所有写入锁都已经释放后,才允许重入 reader 使用它们。 此外,writer 可以获取读取锁,但反过来则不成立。在其他应用程序中,当在调用或回调那些在读取锁状态下执行读取操作的方法期间保持写入锁时,重入很有用。如果 reader 试图获取写入锁,那么将永远不会获得成功。 

锁降级 
重入还允许从写入锁降级为读取锁,其实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,从读取锁升级到写入锁是不可能的。 

锁获取的中断 
读取锁和写入锁都支持锁获取期间的中断。 

下面的代码展示了如何利用重入来执行升级缓存后的锁降级(为简单起见,省略了异常处理):

Java代码  收藏代码
  1. class CachedData {  
  2.    Object data;  
  3.    volatile boolean cacheValid;  
  4.    ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();  
  5.   
  6.    void processCachedData() {  
  7.      rwl.readLock().lock();  
  8.      if (!cacheValid) {  
  9.         // Must release read lock before acquiring write lock  
  10.         rwl.readLock().unlock();  
  11.         rwl.writeLock().lock();  
  12.         // Recheck state because another thread might have acquired  
  13.         //   write lock and changed state before we did.  
  14.         if (!cacheValid) {  
  15.           data = ...  
  16.           cacheValid = true;  
  17.         }  
  18.         // Downgrade by acquiring read lock before releasing write lock  
  19.         rwl.readLock().lock();  
  20.         rwl.writeLock().unlock(); // Unlock write, still hold read  
  21.      }  
  22.   
  23.      use(data);  
  24.      rwl.readLock().unlock();  
  25.    }  
  26.  }  

 

下面为用读-写锁来包装的Map类:

Java代码  收藏代码
  1. /** 
  2.  * Huisou.com Inc. 
  3.  * Copyright (c) 2011-2012 All Rights Reserved. 
  4.  */  
  5.   
  6. package thread;  
  7.   
  8. import com.sun.org.apache.bcel.internal.generic.NEW;  
  9.   
  10. import java.util.Map;  
  11. import java.util.concurrent.locks.Lock;  
  12. import java.util.concurrent.locks.ReadWriteLock;  
  13. import java.util.concurrent.locks.ReentrantReadWriteLock;  
  14.   
  15. /** 
  16.  * @description 
  17.  *  
  18.  * @author chenzehe 
  19.  * @email hljuczh@163.com 
  20.  * @create 2013-1-9 下午09:05:58 
  21.  */  
  22.   
  23. public class ReadWriteMap<K, V> {  
  24.     private final Map<K, V>       map;  
  25.     private final ReadWriteLock lock    = new ReentrantReadWriteLock();  
  26.     private final Lock          r       = lock.readLock();  
  27.     private final Lock          w       = lock.writeLock();  
  28.       
  29.     public ReadWriteMap(Map<K, V> map) {  
  30.         this.map = map;  
  31.     }  
  32.       
  33.     public V put(K key, V value) {  
  34.         w.lock();  
  35.         try {  
  36.             return map.put(key, value);  
  37.         }  
  38.         finally {  
  39.             w.unlock();  
  40.         }  
  41.     }  
  42.       
  43.     public V get(Object key) {  
  44.         r.lock();  
  45.         try {  
  46.             return map.get(key);  
  47.         }  
  48.         finally {  
  49.             r.unlock();  
  50.         }  
  51.     }  
  52. }  

 

Condition

 

     条件(也称为条件队列 或条件变量 )为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。

Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其newCondition() 方法。如下代码假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take 操作,则在items有可用数据之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞,如下:

Java代码  收藏代码
  1. class BoundedBuffer {  
  2.   final Lock lock = new ReentrantLock();  
  3.   final Condition notFull  = lock.newCondition();   
  4.   final Condition notEmpty = lock.newCondition();   
  5.   
  6.   final Object[] items = new Object[100];  
  7.   int putptr, takeptr, count;  
  8.   
  9.   public void put(Object x) throws InterruptedException {  
  10.     lock.lock();  
  11.     try {  
  12.       while (count == items.length)   
  13.         notFull.await();  
  14.       items[putptr] = x;   
  15.       if (++putptr == items.length) putptr = 0;  
  16.       ++count;  
  17.       notEmpty.signal();  
  18.     } finally {  
  19.       lock.unlock();  
  20.     }  
  21.   }  
  22.   
  23.   public Object take() throws InterruptedException {  
  24.     lock.lock();  
  25.     try {  
  26.       while (count == 0)   
  27.         notEmpty.await();  
  28.       Object x = items[takeptr];   
  29.       if (++takeptr == items.length) takeptr = 0;  
  30.       --count;  
  31.       notFull.signal();  
  32.       return x;  
  33.     } finally {  
  34.       lock.unlock();  
  35.     }  
  36.   }   
  37. }  

 双向双端链表LinkedBlockingDeque也是通过实现一个ReentrantLock对象和两个Condition对象来实现阻塞同步”的:

 

Java代码  收藏代码
  1.     /** Main lock guarding all access */  
  2.     private final ReentrantLock lock = new ReentrantLock();  
  3.     /** Condition for waiting takes */  
  4.     private final Condition notEmpty = lock.newCondition();  
  5.     /** Condition for waiting puts */  
  6.     private final Condition notFull = lock.newCondition();  
  7.   
  8. ......  
  9.   
  10.     /** 
  11.      * @throws NullPointerException {@inheritDoc} 
  12.      * @throws InterruptedException {@inheritDoc} 
  13.      */  
  14.     public void putLast(E e) throws InterruptedException {  
  15.         if (e == nullthrow new NullPointerException();  
  16.         lock.lock();  
  17.         try {  
  18.             while (!linkLast(e))  
  19.                 notFull.await();  
  20.         } finally {  
  21.             lock.unlock();  
  22.         }  
  23.     }  
  24.   
  25. ......  
  26.   
  27.     /** 
  28.      * Links e as last element, or returns false if full. 
  29.      */  
  30.     private boolean linkLast(E e) {  
  31.         if (count >= capacity)  
  32.             return false;  
  33.         ++count;  
  34.         Node<E> l = last;  
  35.         Node<E> x = new Node<E>(e, l, null);  
  36.         last = x;  
  37.         if (first == null)  
  38.             first = x;  
  39.         else  
  40.             l.next = x;  
  41.         notEmpty.signal();  
  42.         return true;  
  43.     }  

 

分享到:
评论

相关推荐

    pandas-1.3.5-cp37-cp37m-macosx_10_9_x86_64.zip

    pandas whl安装包,对应各个python版本和系统(具体看资源名字),找准自己对应的下载即可! 下载后解压出来是已.whl为后缀的安装包,进入终端,直接pip install pandas-xxx.whl即可,非常方便。 再也不用担心pip联网下载网络超时,各种安装不成功的问题。

    基于java的大学生兼职信息系统答辩PPT.pptx

    基于java的大学生兼职信息系统答辩PPT.pptx

    基于java的乐校园二手书交易管理系统答辩PPT.pptx

    基于java的乐校园二手书交易管理系统答辩PPT.pptx

    tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl

    tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl

    Android Studio Ladybug(android-studio-2024.2.1.10-mac.zip.002)

    Android Studio Ladybug 2024.2.1(android-studio-2024.2.1.10-mac.dmg)适用于macOS Intel系统,文件使用360压缩软件分割成两个压缩包,必须一起下载使用: part1: https://download.csdn.net/download/weixin_43800734/89954174 part2: https://download.csdn.net/download/weixin_43800734/89954175

    基于ssm框架+mysql+jsp实现的监考安排与查询系统

    有学生和教师两种角色 登录和注册模块 考场信息模块 考试信息模块 点我收藏 功能 监考安排模块 考场类型模块 系统公告模块 个人中心模块: 1、修改个人信息,可以上传图片 2、我的收藏列表 账号管理模块 服务模块 eclipse或者idea 均可以运行 jdk1.8 apache-maven-3.6 mysql5.7及以上 tomcat 8.0及以上版本

    tornado-6.1b2-cp38-cp38-macosx_10_9_x86_64.whl

    tornado-6.1b2-cp38-cp38-macosx_10_9_x86_64.whl

    Android Studio Ladybug(android-studio-2024.2.1.10-mac.zip.001)

    Android Studio Ladybug 2024.2.1(android-studio-2024.2.1.10-mac.dmg)适用于macOS Intel系统,文件使用360压缩软件分割成两个压缩包,必须一起下载使用: part1: https://download.csdn.net/download/weixin_43800734/89954174 part2: https://download.csdn.net/download/weixin_43800734/89954175

    基于MATLAB车牌识别代码实现代码【含界面GUI】.zip

    matlab

    基于java的毕业生就业信息管理系统答辩PPT.pptx

    基于java的毕业生就业信息管理系统答辩PPT.pptx

    基于Web的毕业设计选题系统的设计与实现(springboot+vue+mysql+说明文档).zip

    随着高等教育的普及和毕业设计的日益重要,为了方便教师、学生和管理员进行毕业设计的选题和管理,我们开发了这款基于Web的毕业设计选题系统。 该系统主要包括教师管理、院系管理、学生管理等多个模块。在教师管理模块中,管理员可以新增、删除教师信息,并查看教师的详细资料,方便进行教师资源的分配和管理。院系管理模块则允许管理员对各个院系的信息进行管理和维护,确保信息的准确性和完整性。 学生管理模块是系统的核心之一,它提供了学生选题、任务书管理、开题报告管理、开题成绩管理等功能。学生可以在此模块中进行毕业设计的选题,并上传任务书和开题报告,管理员和教师则可以对学生的报告进行审阅和评分。 此外,系统还具备课题分类管理和课题信息管理功能,方便对毕业设计课题进行分类和归档,提高管理效率。在线留言功能则为学生、教师和管理员提供了一个交流互动的平台,可以就毕业设计相关问题进行讨论和解答。 整个系统设计简洁明了,操作便捷,大大提高了毕业设计的选题和管理效率,为高等教育的发展做出了积极贡献。

    机器学习(预测模型):2000年至2015年期间193个国家的预期寿命和相关健康因素的数据

    这个数据集来自世界卫生组织(WHO),包含了2000年至2015年期间193个国家的预期寿命和相关健康因素的数据。它提供了一个全面的视角,用于分析影响全球人口预期寿命的多种因素。数据集涵盖了从婴儿死亡率、GDP、BMI到免疫接种覆盖率等多个维度,为研究者提供了丰富的信息来探索和预测预期寿命。 该数据集的特点在于其跨国家的比较性,使得研究者能够识别出不同国家之间预期寿命的差异,并分析这些差异背后的原因。数据集包含22个特征列和2938行数据,涉及的变量被分为几个大类:免疫相关因素、死亡因素、经济因素和社会因素。这些数据不仅有助于了解全球健康趋势,还可以辅助制定公共卫生政策和社会福利计划。 数据集的处理包括对缺失值的处理、数据类型转换以及去重等步骤,以确保数据的准确性和可靠性。研究者可以使用这个数据集来探索如教育、健康习惯、生活方式等因素如何影响人们的寿命,以及不同国家的经济发展水平如何与预期寿命相关联。此外,数据集还可以用于预测模型的构建,通过回归分析等统计方法来预测预期寿命。 总的来说,这个数据集是研究全球健康和预期寿命变化的宝贵资源,它不仅提供了历史数据,还为未来的研究和政策制

    基于微信小程序的高校毕业论文管理系统小程序答辩PPT.pptx

    基于微信小程序的高校毕业论文管理系统小程序答辩PPT.pptx

    基于java的超市 Pos 收银管理系统答辩PPT.pptx

    基于java的超市 Pos 收银管理系统答辩PPT.pptx

    基于java的网上报名系统答辩PPT.pptx

    基于java的网上报名系统答辩PPT.pptx

    基于java的网上书城答辩PPT.pptx

    基于java的网上书城答辩PPT.pptx

    婚恋网站 SSM毕业设计 附带论文.zip

    婚恋网站 SSM毕业设计 附带论文 启动教程:https://www.bilibili.com/video/BV1GK1iYyE2B

    基于java的戒烟网站答辩PPT.pptx

    基于java的戒烟网站答辩PPT.pptx

    基于微信小程序的“健康早知道”微信小程序答辩PPT.pptx

    基于微信小程序的“健康早知道”微信小程序答辩PPT.pptx

    机器学习(预测模型):自行车共享使用情况的数据集

    Capital Bikeshare 数据集是一个包含从2020年5月到2024年8月的自行车共享使用情况的数据集。这个数据集记录了华盛顿特区Capital Bikeshare项目中自行车的租赁模式,包括了骑行的持续时间、开始和结束日期时间、起始和结束站点、使用的自行车编号、用户类型(注册会员或临时用户)等信息。这些数据可以帮助分析和预测自行车共享系统的需求模式,以及了解用户行为和偏好。 数据集的特点包括: 时间范围:覆盖了四年多的时间,提供了长期的数据观察。 细节丰富:包含了每次骑行的详细信息,如日期、时间、天气条件、季节等,有助于深入分析。 用户分类:数据中区分了注册用户和临时用户,可以分析不同用户群体的使用习惯。 天气和季节因素:包含了天气情况和季节信息,可以研究这些因素对骑行需求的影响。 通过分析这个数据集,可以得出关于自行车共享使用模式的多种见解,比如一天中不同时间段的使用高峰、不同天气条件下的使用差异、季节性变化对骑行需求的影响等。这些信息对于城市规划者、交通管理者以及自行车共享服务提供商来说都是非常宝贵的,可以帮助他们优化服务、提高效率和满足用户需求。同时,这个数据集也

Global site tag (gtag.js) - Google Analytics