




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Java 并发编程深入学习 Lock 锁Lock 锁介绍在 Java 5.0 之前,在协调对共享对象的访问时可以使用的机制只有synchronized 和 volatile 。Java 5.0 增加了一种新的机制: ReentrantLock. 它并不是一种替代内置加锁的方法, 而是当内 置加锁机制不适用时,作为一种可选择的高级功能。Lock 接口Lock 接口位于 java.util.concurrent.locks 包中,它定义了一组抽象的加锁操作。public interface Lock /获取锁void lock();/ 如果当前线程未被中断,则获取锁void lockInterru
2、ptibly() throws InterruptedException;/仅在调用时锁为空闲状态才获取该锁如果锁可用,则获取锁,并立即返回值true。如果锁不可用,则此方法将立即返回值falseboolean tryLock();/如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁boolean tryLock(long time, TimeUnit unit) throws InterruptedException;/释放锁void unlock();/返回绑定到此 Lock 实例的新 Condition 实例Condition newCondition();ReetrantLo
3、ck 实现了 Lock 接口,并提供了与 synchronized 相同的互斥性和内存可见性, 在获取 ReentrantLock 时,有着与进入同步代码块相同的内存语义, 在释放 ReentrantLock 时, 同样有着与退出同步代码块相同的内存语义。lock 锁与 synchronized 锁对比为什么要创建一种与内置锁如此相似的新加锁机制?在大多数情况下,内置锁都能很好地工作,但在功能上存在一些局限性。内置锁无法中断一个正在等待获取锁的线程,或者无法在请求获取一个锁时无限地等待下去。内置锁必须在获取该锁的代码块中释放, 这虽然简化了编码工作, 并且与异常处理操作实现 了很好的交互,但却
4、无法实现非阻塞结构的加锁规则。所以需要一种更加灵活的加锁机制, lock 锁便应运而生。Lock 锁的标准使用形式如下:Lock lock = new ReentrantLock();if (lock.tryLock() / 尝试获取锁try /更新对象状态/捕获异常,并在必要时恢复不变性条件 finally lock.unlock();/ 注意要记得释放锁 else / 获取锁失败执行其他操作如果没有使用 finally 来释放 Lock, 那么程序出错时,将很难追踪到最初发生错误的位置,因 为没有记录应该释放锁的位置和时间。这一点也是 ReetrantLock 不能完全替代 synchro
5、nized 的原因, 因为它更加危险,程序并没有 自动清除锁的机制,使用起来需要格外小心。锁的分类1. 可重入锁当某一个线程请求一个由其他线程持有的锁时, 发去请求的线程就会阻塞。 由于内置锁可 重入特性的存在, 如果某个线程视图获得一个已经由它自己持有的锁, 那么这个请求却会成 功。如果锁具备可重入性,则称作为可重入锁。 .像 synchronized 和 ReentrantLock 都是可重入 锁,重入性表明了锁的分配机制:基于线程的分配,而不是基于方法调用的分配。重入锁的实现机制如下:为每个锁关联一个获取计数值和一个所有者线程。 当计数器为 0 时这个锁被认为没有被任 何线程持有。当线程
6、请求一个未被持有的锁时, JVM 将记下锁的持有者,并且将获取计数 器置为 1。如果同一个线程再次获取这个锁,计数器将递增,而当线程退出同步代码块时, 计数器会相应地递减。当计数器为0 时,这个锁将被释放。可举个简单的例子,当一个线程执行到某个synchronized方法时,比如说 methodi,而在methodi中会调用另外一个synchronized方法method2,此时线程不必重新去申请锁,而是可以直接执行方法method2。看下面这段代码就明白了:class MyClass public synchronized void method1() method2();public sy
7、nchronized void method2() 上述代码中的两个方法 method1 和 method2 都用 synchronized 修饰了,假如某一时刻, 线程 A 执行到了 method1 ,此时线程 A 获取了这个对象的锁,而由于 method2 也是 synchronized 方法,假如 synchronized 不具备可重入性,此时线程 A 需要重新申请锁。但是 这就会造成一个问题,因为线程 A 已经持有了该对象的锁,而又在申请获取该对象的锁, 这样就会线程 A 一直等待永远不会获取到的锁。而由于 synchronized 和 Lock 都具备可重入性,所以不会发生上述现象。
8、2. 可中断锁可中断锁:顾名思义,就是可以相应中断的锁。在 Java 中, synchronized 就不是可中断锁,而 Lock 是可中断锁。如果某一线程 A 正在执行锁中的代码,另一线程 B 正在等待获取该锁,可能由于等待 时间过长,线程 B 不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线 程中中断它,这种就是可中断锁。下面的例子展示了中断锁的场景。public class TestLockInterrupt private static Lock lock = new ReentrantLock();public static void main(String args)
9、 final TestLockInterrupt test = new TestLockInterrupt();Thread ta = new Thread() Overridepublic void run() System.out.println(A 线程启动了 !- 准备打印 );try test.print(A, aaaa); catch (InterruptedException e) System.out.println(A 线程收到中断异常 );Thread tb = new Thread() Overridepublic void run() System.out.printl
10、n(B 线程启动了 !- 准备打印 .); try test.print(B, bbbb); catch (InterruptedException e) System.out.println(B 线程收到中断异常 );ta.start();tb.start();try Thread.sleep(2000); catch (InterruptedException e) e.printStackTrace();/判定 Lock 是否还被某个线程持有if (ReentrantLock) lock).isLocked() System.out.println( 等了两秒还没有获得锁 ,直接中断 !
11、); errupt();public void print(String tName, String content) throws InterruptedException lock.lockInterruptibly();try /lock.lock();System.out.println( 线程 + tName + 获取锁并打印内容 + content); / 模拟耗时操作 ,使某个线程能够在较长时间独占锁Thread.currentThread().sleep(5000);/int i = 1;/ while (i 准备打印 线程 A 获取锁并打印内容 aaaaB 线程启
12、动了 !- 准备打印 . 等了两秒还没有获得锁 ,直接中断 !B 线程收到中断异常线程 A 释放了锁3. 公平锁公平锁即尽量以请求锁的顺序来获取锁。 比如同是有多个线程在等待一个锁, 当这个锁 被释放时,等待时间最久的线程(最先请求的线程)会获得该锁,这种就是公平锁。非公平锁即无法保证锁的获取是按照请求锁的顺序进行的。 这样就可能导致某个或者一 些线程永远获取不到锁。在 Java 中, synchronized 就是非公平锁,它无法保证等待的线程获取锁的顺序。而对于 ReentrantLock 和 ReentrantReadWriteLock ,它默认情况下是非公平锁, 但是可以 设置为公平锁
13、。在 ReentrantLock 中定义了 2 个静态内部类,一个是 NotFairSync ,一个是 FairS ync,分别用来实现非公平锁和公平锁。这两个类的定义如下:* Sync object for non-fair locks*/static final class NonfairSync extends Sync private static final long serialVersionUID = 7316153563782823691L;/* Performs lock. Try immediate barge, backing up to normal* acquire
14、on failure.*/final void lock() if (compareAndSetState(0, 1) setExclusiveOwnerThread(Thread.currentThread();elseacquire(1);protected final boolean tryAcquire(int acquires) return nonfairTryAcquire(acquires);* Sync object for fair locks*/static final class FairSync extends Sync private static final lo
15、ng serialVersionUID = -3000897897090466540L;final void lock() acquire(1);* Fair version of tryAcquire.Dont grant access unless* recursive call or no waiters or is first.*/protected final boolean tryAcquire(int acquires) final Thread current = Thread.currentThread();int c = getState();if (c = 0) if (
16、!hasQueuedPredecessors() & compareAndSetState(0, acquires) setExclusiveOwnerThread(current); return true;else if (current = getExclusiveOwnerThread() int nextc = c + acquires;if (nextc 0)throw new Error(Maximum lock count exceeded); setState(nextc);return true;return false;我们可以在创建 ReentrantLock 对象时,
17、通过以下方式来设置锁的公平性:ReentrantLock lock = new ReentrantLock(true);1如果参数为 true 表示为公平锁,为 fasle 为非公平锁。默认情况下,如果使用无参构造 器,则是非公平锁。其他常用方法另外在 ReentrantLock 类中定义了很多方法,比如:isFair()/判断锁是否是公平锁isLocked()/ 判断锁是否被任何线程获取了isHeldByCurrentThread() / 判断锁是否被当前线程获取了 hasQueuedThreads() / 判断是否有线程在等待该锁在 ReentrantReadWriteLock 中也有类似
18、的方法,同样也可以设置为公平锁和非公平锁。不过 要记住, ReentrantReadWriteLock 并未实现 Lock 接口,它实现的是 ReadWriteLock 接口。4. 读写锁读写锁将对一个资源(比如文件)的访问分成了 2 个锁,一个读锁和一个写锁。 正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。ReadWriteLock 就是读写锁,它是一个接口, ReentrantReadWriteLock 实现了这个接口。 这个接口定义如下:public interface ReadWriteLock /获取读锁Lock readLock();/获取写锁Lock writeLoc
19、k();面的例子展示了读写锁的一些基本用法和特性。public class TestMain public static void main(String args) ReadWriteLock lock = new ReentrantReadWriteLock(); final Lock readLock = lock.readLock(); final Lock writeLock = lock.writeLock(); final Resource resource = new Resource(); final Random random = new Random(); for (in
20、t i = 0; i 20; +i)/ 写线程 new Thread() public void run() writeLock.lock(); try resource.setValue(resource.getValue() + 1);System.out.println(newSmpleDateFormat(yyyy-MM-dd HH:mm:ss.SSS).format(new Date() + - + Thread.currentThread() + 获取了写锁, 为: + resource.getValue();Thread.sleep(random.nextInt(1000);/
21、随机休眠 catch (Exception e) e.printStackTrace(); finally writeLock.unlock();.start(); for (int i = 0; i 20; +i)/ 读线程 new Thread() public void run() readLock.lock(); try System.out.println(newSimpleDateFormat(yyyy-MM-dd HH:mm:ss.SSS).format(new Date() + - + Thread.currentThread() + 获取了读锁, 据为: + resource
22、.getValue();Thread.sleep(random.nextInt(1000);/ 随机休眠 catch (Exception e) e.printStackTrace();修正数据读取的数 finally readLock.unlock();.start(); /资源类定义 class Resource private int value;public void setValue(int value) this.value = value;public int getValue() return value;运行结果2016-09-13 10:16:59.947 - Thread
23、Thread-0,5,main 获取了写锁,修正数据为: 12016-09-13 10:17:00.829 - ThreadThread-1,5,main 获取了写锁,修正数据为: 22016-09-13 10:17:01.502 - ThreadThread-2,5,main 获取了写锁,修正数据为: 32016-09-13 10:17:01.952 - ThreadThread-3,5,main 获取了写锁,修正数据为: 42016-09-13 10:17:02.641 - ThreadThread-4,5,main 获取了写锁,修正数据为: 52016-09-13 10:17:03.38
24、9 - ThreadThread-5,5,main 获取了写锁,修正数据为: 62016-09-13 10:17:04.380 - ThreadThread-6,5,main 获取了写锁,修正数据为: 72016-09-13 10:17:05.377 - ThreadThread-7,5,main 获取了写锁,修正数据为: 82016-09-13 10:17:06.306 - ThreadThread-8,5,main 获取了写锁,修正数据为: 92016-09-13 10:17:06.470 - ThreadThread-9,5,main 获取了写锁,修正数据为: 102016-09-13
25、10:17:06.696 - ThreadThread-10,5,main 获取了写锁,修正数据为: 112016-09-13 10:17:06.911 - ThreadThread-11,5,main 获取了写锁,修正数据为: 122016-09-13 10:17:07.141 - ThreadThread-12,5,main 获取了写锁,修正数据为: 132016-09-13 10:17:07.170 - ThreadThread-13,5,main 获取了写锁,修正数据为: 142016-09-13 10:17:07.449 - ThreadThread-14,5,main 获取了写锁,
26、修正数据为: 152016-09-13 10:17:07.939 - ThreadThread-15,5,main 获取了写锁,修正数据为: 162016-09-13 10:17:08.252 - ThreadThread-16,5,main 获取了写锁,修正数据为: 172016-09-13 10:17:08.798 - ThreadThread-17,5,main 获取了写锁,修正数据为: 182016-09-13 10:17:09.119 - ThreadThread-18,5,main 获取了写锁,修正数据为: 192016-09-13 10:17:09.353 - ThreadThr
27、ead-19,5,main 获取了写锁,修正数据为: 202016-09-13 10:17:10.336 - ThreadThread-20,5,main 获取了读锁,读取的数据为: 202016-09-13 10:17:10.336 - ThreadThread-21,5,main 获取了读锁,读取的数据为: 202016-09-13 10:17:10.336 - ThreadThread-22,5,main 获取了读锁,读取的数据为: 202016-09-13 10:17:10.336 - ThreadThread-23,5,main 获取了读锁,读取的数据为: 202016-09-13 10:17:10.336 - ThreadThread-24,5,main 获取了读锁,读取的数据为: 202016-09-13 10:17:10.336 - ThreadThread-25,5,main 获取了读锁,读取的数据为: 202016-09-13 10:17:10.336 - ThreadThread-26,5,main 获取了读锁,读取的数据为: 202016-09-13 10:17:10.336 - Thr
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 餐饮企业股份合作投资合同
- 草籽草坪种植与景观设计一体化合同
- 跨境电商平台进口采购合同多语言翻译及品牌推广协议
- 商铺租赁合同含装修标准及配套设施要求
- 2025医院护士工作心得体会(20篇)
- 情境试题网络营销
- 建筑行业试题集设计
- 全新快递合作合同书
- 水仙花赞写物作文13篇
- 全面统筹公路工程考试试题及答案内容
- 统编版语文三年级上册第四单元快乐读书吧:在那奇妙的王国里 课件
- 第二届全国化工和医药行业安全生产线上知识竞赛题库(共150题)
- 数据采集与分析服务协议
- 2025年北京市朝阳区九年级初三二模道德与法治试卷(含答案)
- 第2章 第2节 五行学说课件
- 国家开放大学国开电大《统计与数据分析基础》形考任务1-4 参考答案
- (高清版)DG∕TJ 08-2251-2018 消防设施物联网系统技术标准
- 河南省青桐鸣大联考普通高中2024-2025学年高三考前适应性考试英语试题及答案
- 导电高分子课件:探索导电材料的秘密
- 2025年成人高考《语文》文学常识经典题型与历年真题试卷
- 浙江开放大学2025年《社会保障学》形考任务4答案
评论
0/150
提交评论