Java并发编程深入学习讲解Word格式文档下载.docx
- 文档编号:16403634
- 上传时间:2022-11-23
- 格式:DOCX
- 页数:12
- 大小:19.60KB
Java并发编程深入学习讲解Word格式文档下载.docx
《Java并发编程深入学习讲解Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《Java并发编程深入学习讲解Word格式文档下载.docx(12页珍藏版)》请在冰豆网上搜索。
内置锁必须在获取该锁的代码块中释放,这虽然简化了编码工作,并且与异常处理操作实现了很好的交互,但却无法实现非阻塞结构的加锁规则。
所以需要一种更加灵活的加锁机制,lock锁便应运而生。
Lock锁的标准使用形式如下:
Locklock=newReentrantLock();
if(lock.tryLock()){//尝试获取锁
try{
//更新对象状态
//捕获异常,并在必要时恢复不变性条件
}finally{
lock.unlock();
//注意要记得释放锁
}
}else{
//获取锁失败执行其他操作
如果没有使用finally来释放Lock,那么程序出错时,将很难追踪到最初发生错误的位置,因为没有记录应该释放锁的位置和时间。
这一点也是ReetrantLock不能完全替代synchronized的原因,因为它更加危险,程序并没有自动清除锁的机制,使用起来需要格外小心。
锁的分类
1.可重入锁
当某一个线程请求一个由其他线程持有的锁时,发去请求的线程就会阻塞。
由于内置锁可重入特性的存在,如果某个线程视图获得一个已经由它自己持有的锁,那么这个请求却会成功。
如果锁具备可重入性,则称作为可重入锁。
.像synchronized和ReentrantLock都是可重入锁,重入性表明了锁的分配机制:
基于线程的分配,而不是基于方法调用的分配。
重入锁的实现机制如下:
为每个锁关联一个获取计数值和一个所有者线程。
当计数器为0时这个锁被认为没有被任何线程持有。
当线程请求一个未被持有的锁时,JVM将记下锁的持有者,并且将获取计数器置为1。
如果同一个线程再次获取这个锁,计数器将递增,而当线程退出同步代码块时,计数器会相应地递减。
当计数器为0时,这个锁将被释放。
可举个简单的例子,当一个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法method2,此时线程不必重新去申请锁,而是可以直接执行方法method2。
看下面这段代码就明白了:
classMyClass{
publicsynchronizedvoidmethod1(){
method2();
publicsynchronizedvoidmethod2(){
上述代码中的两个方法method1和method2都用synchronized修饰了,假如某一时刻,线程A执行到了method1,此时线程A获取了这个对象的锁,而由于method2也是synchronized方法,假如synchronized不具备可重入性,此时线程A需要重新申请锁。
但是这就会造成一个问题,因为线程A已经持有了该对象的锁,而又在申请获取该对象的锁,这样就会线程A一直等待永远不会获取到的锁。
而由于synchronized和Lock都具备可重入性,所以不会发生上述现象。
2.可中断锁
可中断锁:
顾名思义,就是可以相应中断的锁。
在Java中,synchronized就不是可中断锁,而Lock是可中断锁。
如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。
下面的例子展示了中断锁的场景。
publicclassTestLockInterrupt{
privatestaticLocklock=newReentrantLock();
publicstaticvoidmain(String[]args){
finalTestLockInterrupttest=newTestLockInterrupt();
Threadta=newThread(){
@Override
publicvoidrun(){
System.out.println("
A线程启动了!
->
准备打印...."
);
test.print("
A"
"
aaaa"
}catch(InterruptedExceptione){
A线程收到中断异常"
};
Threadtb=newThread(){
B线程启动了!
准备打印..."
B"
bbbb"
B线程收到中断异常"
ta.start();
tb.start();
Thread.sleep(2000);
e.printStackTrace();
//判定Lock是否还被某个线程持有
if(((ReentrantLock)lock).isLocked()){
等了两秒还没有获得锁,直接中断!
"
tb.interrupt();
publicvoidprint(StringtName,Stringcontent)throwsInterruptedException{
lock.lockInterruptibly();
//lock.lock();
线程"
+tName+"
获取锁并打印内容"
+content);
//模拟耗时操作,使某个线程能够在较长时间独占锁
Thread.currentThread().sleep(5000);
//inti=1;
//while(i<
1000000000){
//i++;
//}
释放了锁"
运行结果
准备打印....
线程A获取锁并打印内容aaaa
准备打印...
B线程收到中断异常
线程A释放了锁
3.公平锁
公平锁即尽量以请求锁的顺序来获取锁。
比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该锁,这种就是公平锁。
非公平锁即无法保证锁的获取是按照请求锁的顺序进行的。
这样就可能导致某个或者一些线程永远获取不到锁。
在Java中,synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。
而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。
在ReentrantLock中定义了2个静态内部类,一个是NotFairSync,一个是FairSync,分别用来实现非公平锁和公平锁。
这两个类的定义如下:
/**
*Syncobjectfornon-fairlocks
*/
staticfinalclassNonfairSyncextendsSync{
privatestaticfinallongserialVersionUID=7316153563782823691L;
*Performslock.Tryimmediatebarge,backinguptonormal
*acquireonfailure.
finalvoidlock(){
if(compareAndSetState(0,1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire
(1);
protectedfinalbooleantryAcquire(intacquires){
returnnonfairTryAcquire(acquires);
*Syncobjectforfairlocks
staticfinalclassFairSyncextendsSync{
privatestaticfinallongserialVersionUID=-3000897897090466540L;
*FairversionoftryAcquire.Don'
tgrantaccessunless
*recursivecallornowaitersorisfirst.
finalThreadcurrent=Thread.currentThread();
intc=getState();
if(c==0){
if(!
hasQueuedPredecessors()&
&
compareAndSetState(0,acquires)){
setExclusiveOwnerThread(current);
returntrue;
elseif(current==getExclusiveOwnerThread()){
intnextc=c+acquires;
if(nextc<
0)
thrownewError("
Maximumlockcountexceeded"
setState(nextc);
returnfalse;
我们可以在创建ReentrantLock对象时,通过以下方式来设置锁的公平性:
ReentrantLocklock=newReentrantLock(true);
1
如果参数为true表示为公平锁,为fasle为非公平锁。
默认情况下,如果使用无参构造器,则是非公平锁。
其他常用方法
另外在ReentrantLock类中定义了很多方法,比如:
isFair()//判断锁是否是公平锁
isLocked()//判断锁是否被任何线程获取了
isHeldByCurrentThread()//判断锁是否被当前线程获取了
hasQueuedThreads()//判断是否有线程在等待该锁
在ReentrantReadWriteLock中也有类似的方法,同样也可以设置为公平锁和非公平锁。
不过要记住,ReentrantReadWriteLock并未实现Lock接口,它实现的是ReadWriteLock接口。
4.读写锁
读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。
正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。
ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。
这个接口定义如下:
publicinterfaceReadWriteLock{
//获取读锁
LockreadLock();
//获取写锁
LockwriteLock();
下面的例子展示了读写锁的一些基本用法和特性。
publicclassTestMain{
ReadWriteLocklock=newReentrantReadWriteLock();
finalLockreadLock=lock.readLock();
finalLockwriteLock=lock.writeLock();
finalResourceresource=newResource();
finalRandomrandom=newRandom();
for(inti=0;
i<
20;
++i)
{//写线程
newThread(){
writeLock.lock();
resource.setValue(resource.getValue()+1);
System.out.println(newSmpleDateFormat("
yyyy-MM-ddHH:
mm:
ss.SSS"
).format(newDate())+"
-"
+Thread.currentThread()+"
获取了写锁,修正数据为:
+resource.getValue());
Thread.sleep(random.nextInt(1000));
//随机休眠
}catch(Exceptione){
writeLock.unlock();
}.start();
{//读线程
readLock.lock();
System.out.println(newSimpleDateFormat("
获取了读锁,读取的数据为:
readLock.unlock();
//资源类定义
classResource{
privateintvalue;
publicvoidsetValue(intvalue){
this.value=value;
publicintgetValue(){
returnvalue;
2016-09-1310:
16:
59.947-Thread[Thread-0,5,main]获取了写锁,修正数据为:
17:
00.829-Thread[Thread-1,5,main]获取了写锁,修正数据为:
2
01.502-Thread[Thread-2,5,main]获取了写锁,修正数据为:
3
01.952-Thread[Thread-3,5,main]获取了写锁,修正数据为:
4
02.641-Thread[Thread-4,5,main]获取了写锁,修正数据为:
5
03.389-Thread[Thread-5,5,main]获取了写锁,修正数据为:
6
04.380-Thread[Thread-6,5,main]获取了写锁,修正数据为:
7
05.377-Thread[Thread-7,5,main]获取了写锁,修正数据为:
8
06.306-Thread[Thread-8,5,main]获取了写锁,修正数据为:
9
06.470-Thread[Thread-9,5,main]获取了写锁,修正数据为:
10
06.696-Thread[Thread-10,5,main]获取了写锁,修正数据为:
11
06.911-Thread[Thread-11,5,main]获取了写锁,修正数据为:
12
07.141-Thread[Thread-12,5,main]获取了写锁,修正数据为:
13
07.170-Thread[Thread-13,5,main]获取了写锁,修正数据为:
14
07.449-Thread[Thread-14,5,main]获取了写锁,修正数据为:
15
07.939-Thread[Thread-15,5,main]获取了写锁,修正数据为:
16
08.252-Thread[Thread-16,5,main]获取了写锁,修正数据为:
17
08.798-Thread[Thread-17,5,main]获取了写锁,修正数据为:
18
09.119-Thread[Thread-18,5,main]获取了写锁,修正数据为:
19
09.353-Thread[Thread-19,5,main]获取了写锁,修正数据为:
20
10.336-Thread[Thread-20,5,main]获取了读锁,读取的数据为:
10.336-Thread[Thread-21,5,main]获取了读锁,读取的数据为:
10.336-Thread[Thread-22,5,main]获取了读锁,读取的数据为:
10.336-Thread[Thread-23,5,main]获取了读锁,读取的数据为:
10.336-Thread[Thread-24,5,main]获取了读锁,读取的数据为:
10.336-Thread[Thread-25,5,main]获取了读锁,读取的数据为:
10.336-Thread[Thread-26,5,main]获取了读锁,读取的数据为:
10.336-Thread[Thread-27,5,main]获取了读锁,读取的数据为:
10.337-Thread[Thread-28,5,main]获取了读锁,读取的数据为:
10.337-Thread[Thread-29,5,main]获取了读锁,读取的数据为:
10.337-Thread[Thread-30,5,main]获取了读锁,读取的数据为:
10.337-Thread[Thread-31,5,main]获取了读锁,读取的数据为:
10.337-Thread[Thread-32,5,main]获取了读锁,读取的数据为:
10.337-Thread[Thread-33,5,main]获取了读锁,读取的数据为:
10.338-Thread[Thread-34,5,main]获取了读锁,读取的数据为:
10.338-Thread[Thread-35,5,main]获取了读锁,读取的数据为:
10.338-Thread[Thread-36,5,main]获取了读锁,读取的数据为:
10.338-Thread[Thread-37,5,main]获取了读锁,读取的数据为:
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java 并发 编程 深入 学习 讲解