Java并发编程深入学习.docx

上传人:b****5 文档编号:26447476 上传时间:2023-06-19 格式:DOCX 页数:15 大小:19.52KB
下载 相关 举报
Java并发编程深入学习.docx_第1页
第1页 / 共15页
Java并发编程深入学习.docx_第2页
第2页 / 共15页
Java并发编程深入学习.docx_第3页
第3页 / 共15页
Java并发编程深入学习.docx_第4页
第4页 / 共15页
Java并发编程深入学习.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

Java并发编程深入学习.docx

《Java并发编程深入学习.docx》由会员分享,可在线阅读,更多相关《Java并发编程深入学习.docx(15页珍藏版)》请在冰豆网上搜索。

Java并发编程深入学习.docx

Java并发编程深入学习

Java并发编程深入学习——Lock锁

Lock锁介绍

  在Java5.0之前,在协调对共享对象的访问时可以使用的机制只有synchronized和volatile。

Java5.0增加了一种新的机制:

ReentrantLock.它并不是一种替代内置加锁的方法,而是当内置加锁机制不适用时,作为一种可选择的高级功能。

Lock接口

Lock接口位于java.util.concurrent.locks包中,它定义了一组抽象的加锁操作。

publicinterfaceLock{

//获取锁

voidlock();

//如果当前线程未被中断,则获取锁

voidlockInterruptibly()throwsInterruptedException;

//仅在调用时锁为空闲状态才获取该锁

//如果锁可用,则获取锁,并立即返回值true。

如果锁不可用,则此方法将立即返回值false

booleantryLock();

//如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁

booleantryLock(longtime,TimeUnitunit)throwsInterruptedException;

//释放锁

voidunlock();

//返回绑定到此Lock实例的新Condition实例

ConditionnewCondition();

}

  ReetrantLock实现了Lock接口,并提供了与synchronized相同的互斥性和内存可见性,在获取ReentrantLock时,有着与进入同步代码块相同的内存语义,在释放ReentrantLock时,同样有着与退出同步代码块相同的内存语义。

lock锁与synchronized锁对比

为什么要创建一种与内置锁如此相似的新加锁机制?

在大多数情况下,内置锁都能很好地工作,但在功能上存在一些局限性。

内置锁无法中断一个正在等待获取锁的线程,或者无法在请求获取一个锁时无限地等待下去。

内置锁必须在获取该锁的代码块中释放,这虽然简化了编码工作,并且与异常处理操作实现了很好的交互,但却无法实现非阻塞结构的加锁规则。

所以需要一种更加灵活的加锁机制,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线程启动了!

->准备打印....");

try{

test.print("A","aaaa");

}catch(InterruptedExceptione){

System.out.println("A线程收到中断异常");

}

}

};

Threadtb=newThread(){

@Override

publicvoidrun(){

System.out.println("B线程启动了!

->准备打印...");

try{

test.print("B","bbbb");

}catch(InterruptedExceptione){

System.out.println("B线程收到中断异常");

}

}

};

ta.start();

tb.start();

try{

Thread.sleep(2000);

}catch(InterruptedExceptione){

e.printStackTrace();

}

//判定Lock是否还被某个线程持有

if(((ReentrantLock)lock).isLocked()){

System.out.println("等了两秒还没有获得锁,直接中断!

");

tb.interrupt();

}

}

publicvoidprint(StringtName,Stringcontent)throwsInterruptedException{

lock.lockInterruptibly();

try{

//lock.lock();

System.out.println("线程"+tName+"获取锁并打印内容"+content);

//模拟耗时操作,使某个线程能够在较长时间独占锁

Thread.currentThread().sleep(5000);

//inti=1;

//while(i<1000000000){

//i++;

//}

}finally{

lock.unlock();

System.out.println("线程"+tName+"释放了锁");

}

}

}

运行结果

A线程启动了!

->准备打印....

线程A获取锁并打印内容aaaa

B线程启动了!

->准备打印...

等了两秒还没有获得锁,直接中断!

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;

finalvoidlock(){

acquire

(1);

}

/**

*FairversionoftryAcquire.Don'tgrantaccessunless

*recursivecallornowaitersorisfirst.

*/

protectedfinalbooleantryAcquire(intacquires){

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);

returntrue;

}

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{

publicstaticvoidmain(String[]args){

ReadWriteLocklock=newReentrantReadWriteLock();

finalLockreadLock=lock.readLock();

finalLockwriteLock=lock.writeLock();

finalResourceresource=newResource();

finalRandomrandom=newRandom();

for(inti=0;i<20;++i)

{//写线程

newThread(){

publicvoidrun(){

writeLock.lock();

try{

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){

e.printStackTrace();

}finally{

writeLock.unlock();

}

}

}.start();

}

for(inti=0;i<20;++i)

{//读线程

newThread(){

publicvoidrun(){

readLock.lock();

try{

System.out.println(newSimpleDateFormat("yyyy-MM-ddHH:

mm:

ss.SSS").format(newDate())+"-"+Thread.currentThread()+"获取了读锁,读取的数据为:

"+resource.getValue());

Thread.sleep(random.nextInt(1000));//随机休眠

}catch(Exceptione){

e.printStackTrace();

}finally{

readLock.unlock();

}

}

}.start();

}

}

}

//资源类定义

classResource{

privateintvalue;

publicvoidsetValue(intvalue){

this.value=value;

}

publicintgetValue(){

returnvalue;

}

}

运行结果

2016-09-1310:

16:

59.947-Thread[Thread-0,5,main]获取了写锁,修正数据为:

1

2016-09-1310:

17:

00.829-Thread[Thread-1,5,main]获取了写锁,修正数据为:

2

2016-09-1310:

17:

01.502-Thread[Thread-2,5,main]获取了写锁,修正数据为:

3

2016-09-1310:

17:

01.952-Thread[Thread-3,5,main]获取了写锁,修正数据为:

4

2016-09-1310:

17:

02.641-Thread[Thread-4,5,main]获取了写锁,修正数据为:

5

2016-09-1310:

17:

03.389-Thread[Thread-5,5,main]获取了写锁,修正数据为:

6

2016-09-1310:

17:

04.380-Thread[Thread-6,5,main]获取了写锁,修正数据为:

7

2016-09-1310:

17:

05.377-Thread[Thread-7,5,main]获取了写锁,修正数据为:

8

2016-09-1310:

17:

06.306-Thread[Thread-8,5,main]获取了写锁,修正数据为:

9

2016-09-1310:

17:

06.470-Thread[Thread-9,5,main]获取了写锁,修正数据为:

10

2016-09-1310:

17:

06.696-Thread[Thread-10,5,main]获取了写锁,修正数据为:

11

2016-09-1310:

17:

06.911-Thread[Thread-11,5,main]获取了写锁,修正数据为:

12

2016-09-1310:

17:

07.141-Thread[Thread-12,5,main]获取了写锁,修正数据为:

13

2016-09-1310:

17:

07.170-Thread[Thread-13,5,main]获取了写锁,修正数据为:

14

2016-09-1310:

17:

07.449-Thread[Thread-14,5,main]获取了写锁,修正数据为:

15

2016-09-1310:

17:

07.939-Thread[Thread-15,5,main]获取了写锁,修正数据为:

16

2016-09-1310:

17:

08.252-Thread[Thread-16,5,main]获取了写锁,修正数据为:

17

2016-09-1310:

17:

08.798-Thread[Thread-17,5,main]获取了写锁,修正数据为:

18

2016-09-1310:

17:

09.119-Thread[Thread-18,5,main]获取了写锁,修正数据为:

19

2016-09-1310:

17:

09.353-Thread[Thread-19,5,main]获取了写锁,修正数据为:

20

2016-09-1310:

17:

10.336-Thread[Thread-20,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.336-Thread[Thread-21,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.336-Thread[Thread-22,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.336-Thread[Thread-23,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.336-Thread[Thread-24,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.336-Thread[Thread-25,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.336-Thread[Thread-26,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.336-Thread[Thread-27,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.337-Thread[Thread-28,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.337-Thread[Thread-29,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.337-Thread[Thread-30,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.337-Thread[Thread-31,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.337-Thread[Thread-32,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.337-Thread[Thread-33,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.338-Thread[Thread-34,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.338-Thread[Thread-35,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.338-Thread[Thread-36,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.338-Thread[Thread-37,5,main]获取了读锁,读取的数据为:

20

2016-09-1310:

17:

10.

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 简历

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1