您的位置:首页 > 编程语言 > Java开发

java多线程基础---synchronized与ReentrantReadWriteLock的介绍与比较

2017-03-29 18:22 781 查看
(1) 创建一个ReentrantReadWriteLock对象

[java] view
plain copy

private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

(2)抽取读锁和写锁:

[java] view
plain copy

private Lock readLock = rwl.readLock();//得到一个可被多个读操作共用的读锁,但它会排斥所有写操作

private Lock writeLock = rwl.writeLock();//得到一个写锁,它会排斥所有其他的读操作和写操作

(3) 对所有访问者加读锁

[java] view
plain copy

public double getTotalBalance(){

readLock.lock();

try{...};

finally{readLock.unlock();}

}

对所有修改者加写锁

[java] view
plain copy

public void transfer(){

writeLock.lock();

try{...};

finally{writeLock.unlock();}

}

1、ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候

线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,

如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断

如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情

ReentrantLock获取锁定与三种方式:

a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁

b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;

c)tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;

d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断

2、synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中

3、在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态;

可重入的锁ReentrantLock:使用ReentrantLock锁最简单的一个例子:

复制内容到剪贴板

代码:

Lock lock = new ReentrantLock();

try {

lock.lcok();

// do something

} finally {

lock.unlock();

}

上面这段代码,首先创建了一个lock,然后调用它的lock()方法,开启锁定,在最后调用它的unlock()解除锁定。值得注意的时,一般在使用锁时,都应该按上面的风格书写代码,即lock.unlock()最好放在finally块,这样可以防止,执行do something时发生异常后,导致锁永远无法被释放。注意:lock与unlock一定要配对。

到此,还没发现Lock与synchronized有什么不同,Lock与synchronized不同之处主要体现在Lock比synchronized更灵活得多,而这些灵活又主要体现在如下的几个方法

//lock()

tryLock()

tryLock(long timeout, TimeUnit timeUnit)

lockInterruptibly()

//unlock()

A、trylock()方法:如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;

B、tryLock(long timeout, TimeUnit timeUnit)方法:如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;

是不是比synchronized灵活就体现出来了,打个不是很恰当的比分:你现在正在忙于工作,突然感觉内急,于是你跑向洗手间,到门口发现一个“清洁中,暂停使用”的牌牌。没办法,工作又忙,所以你只好先放弃去洗手间回去忙工作,可能如此反复,终于你发现可以进了,于是......

像这样的场景用synchronized你怎么实现?没办法,如果synchronized,当你发现洗手间无法暂时无法进入时,就只能乖乖在门口干等了。而使用trylock()呢,首先你试着去洗手间,发现暂时无法进入(trylock返回false),于是你继续忙你的工作,如此反复,直到可以进入洗手间为止(trylock返回true)。甚至,你非常急,你可以尝试性的在门口等20秒,不行再去忙工作(trylock(20,
TimeUnit.SECONDS);)。

C、lockInterruptibly()方法

lockInterruptibly()方法的执行如下:

如果该锁定没有被另一个线程保持,则获取该锁定并立即返回,将锁定的保持计数设置为 1。

如果当前线程已经保持此锁定,则将保持计数加 1,并且该方法立即返回。

如果锁定被另一个线程保持,则出于线程调度目的,禁用当前线程,并且在发生以下两种情况之一以前,该线程将一直处于休眠状态:

a、锁定由当前线程获得;

b、或者其他某个线程中断当前线程。

如果当前线程获得该锁定,则将锁定保持计数设置为1。

如果当前线程:

a、在进入此方法时已经设置了该线程的中断状态;

b、或者在等待获取锁定的同时被中断。

则抛出 InterruptedException,并且清除当前线程的已中断状态。

即lockInterruptibly()方法允许在等待时由其它线程调用它的Thread.interrupt方法来中断等待而直接返回,这时不再获取锁,而会抛出一个InterruptedException。

ReentrantReadWriteLock可以作为公平锁和非公平锁

公平锁:所有等待锁的线程都保存着一个队列中,当获得锁的线程执行完毕释放锁之后,队列中线程按照先进先出的顺序获取锁

private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);

非公平锁:,所有等待锁的线程都保存着一个队列中,当获得锁的线程执行完毕释放锁之后,JVM会在等待队列中随机挑选一个等待线程获取锁

private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(false);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: