java中锁api_Java锁(ReentrantLock)示例

通常在使用多线程环境时,使用synchronized来保证线程安全。

大多数情况下,synchronized关键字是一个不错的选择,但它有一些缺点导致在Java Concurrency包中包含Lock API。Java 1.5 Concurrency API提供了带有Lock接口的java.util.concurrent.locks包和一些实现类来改进对象锁定机制。

Java Lock API中的一些重要接口和类是:

Lock:它是Lock API的基本接口。它提供了synchronized关键字的所有功能,以及为锁定创建不同条件的其他方法,为线程等待锁定提供超时。一些重要的方法是lock()获取锁,unlock()释放锁,tryLock()等待锁定一段时间,newCondition()创建条件等。

Condition:Condition对象类似于对象等待通知模型,具有创建不同等待集的附加功能。Condition对象始终由Lock对象创建。一些重要的方法是await(),它类似于wait()和signal(),signalAll(),类似于notify()和notifyAll()方法。

ReadWriteLock:它包含一对关联的锁,一个用于只读操作,另一个用于写入。只要没有写入器线程,读锁定可以由多个读取器线程同时保持。写锁是独占的。

ReentrantLock:这是最广泛使用的Lock接口实现类。此类以与synchronized关键字类似的方式实现Lock接口。除了Lock接口实现之外,ReentrantLock还包含一些实用程序方法来获取持有锁的线程,线程等待获取锁等。

synchronized块本质上是可重入的,即如果一个线程锁定了监视器对象,并且另一个同步块需要锁定在同一个监视器对象上,则线程可以进入该代码块。下面通过一个简单的例子来理解这个特性。

public class Test{

public synchronized foo(){

//do something

bar();

}

public synchronized bar(){

//do some more

}

}

如果一个线程进入foo(),它就会锁定Test对象,所以当它尝试执行bar()方法时,允许该线程执行bar()方法,因为它已经在Test对象上持有锁,即如同同步(本)。

Java锁示例 - ReentrantLock

现在让我们看一个简单的例子,使用Java Lock API替换synchronized关键字。

假设有一个Resource类,其中包含一些操作,我们希望它是线程安全的,以及一些不需要线程安全的方法。

public class Resource {

public void doSomething(){

//do some operation, DB read, write etc

}

public void doLogging(){

//logging, no need for thread safety

}

}

现在假设有一个实现Runnable的类,并使用Resource对象。

public class SynchronizedLockExample implements Runnable{

private Resource resource;

public SynchronizedLockExample(Resource r){

this.resource = r;

}

@Override

public void run() {

synchronized (resource) {

resource.doSomething();

}

resource.doLogging();

}

}

请注意,使用synchronized块来获取Resource对象上的锁。我们可以在类中创建一个虚拟对象,并将其用于锁定目的。

现在让我们看看如何使用java Lock API并重写上面的程序而不使用synchronized关键字。并将在java中使用ReentrantLock。

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

public class ConcurrencyLockExample implements Runnable{

private Resource resource;

private Lock lock;

public ConcurrencyLockExample(Resource r){

this.resource = r;

this.lock = new ReentrantLock();

}

@Override

public void run() {

try {

if(lock.tryLock(10, TimeUnit.SECONDS)){

resource.doSomething();

}

} catch (InterruptedException e) {

e.printStackTrace();

}finally{

//release lock

lock.unlock();

}

resource.doLogging();

}

}

正如所看到的,代码中使用tryLock()方法来确保线程只等待确定的时间,如果它没有获得对象的锁定,它只是记录和退出。另一个要注意的重点是使用try-finally块来确保即使doSomething()方法调用抛出任何异常也会释放锁定。

Java Lock 与 synchronized 比较

基于以上细节和程序,可以很容易地得出Java Lock和synchronized之间的以下区别。

Java Lock API为锁定提供了更多的可见性和选项,不像在线程可能最终无限期地等待锁定的同步,可以使用tryLock()来确保线程仅等待特定时间。

synchronized代码更清晰,更易于维护,而使用Lock,需要尝试使用try-finally块来确保即使在lock()和unlock()方法调用之间抛出一些异常也会释放Lock。

synchronized块或方法只能覆盖一种方法,而可以在一种方法中获取锁,并使用Lock API在另一种方法中释放它。

synchronized关键字不提供公平性,而可以在创建ReentrantLock对象时将公平性设置为true,以便最长等待的线程首先获得锁定。

可以为Lock创建不同的条件,不同的线程可以针对不同的条件执行await()。

这就是Java Lock示例,Java中的ReentrantLock以及使用synchronized关键字的比较分析。

¥ 我要打赏

纠错/补充

收藏

加QQ群啦,易百教程官方技术学习群

注意:建议每个人选自己的技术方向加群,同一个QQ最多限加 3 个群。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值