最好的网站建设系统,济南网站建设有限公司,广西网站建设价钱,代运营电商公司在Java 5.0中#xff0c;增加了一个新功能以增强内部锁定功能#xff0c;称为可重入锁定。 在此之前#xff0c;“同步”和“易失性”是实现并发的手段。 public synchronized void doAtomicTransfer(){//enter synchronized block , acquire lock over this object.operat… 在Java 5.0中增加了一个新功能以增强内部锁定功能称为可重入锁定。 在此之前“同步”和“易失性”是实现并发的手段。 public synchronized void doAtomicTransfer(){//enter synchronized block , acquire lock over this object.operation1()operation2();
} // exiting synchronized block, release lock over this object. 同步使用内部锁或监视器。 Java中的每个对象都有一个与之关联的固有锁。 每当线程尝试访问同步的块或方法时它都会获取该对象的固有锁定或监视器。 在使用静态方法的情况下线程获取对类对象的锁定。 就代码编写而言内在锁定机制是一种干净的方法对于大多数用例而言它是非常好的。 那么为什么我们需要显式锁的其他功能 来我们讨论一下。 内部锁定机制可能具有一些功能限制例如 无法中断等待获取锁的线程。 间断锁定 如果不想永远等待就不可能尝试获取锁。 尝试锁定 无法实现非块结构的锁定规则因为必须在获取它们的同一块中释放固有锁。 除此之外ReentrantLock还支持锁轮询和支持超时的可中断锁等待。 ReentrantLock还支持可配置的公平性策略从而允许更灵活的线程调度。 让我们看一下ReentrantLock类实现Lock实现的几种方法 void lock();void lockInterruptibly() throws InterruptedException;boolean tryLock();boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
..... 让我们尝试了解这些用法看看我们可以获得什么好处。 轮询和定时锁获取 让我们看一下示例代码 public void transferMoneyWithSync(Account fromAccount, Account toAccount,float amount) throws InsufficientAmountException {synchronized (fromAccount) {// acquired lock on fromAccount Objectsynchronized (toAccount) {// acquired lock on toAccount Objectif (amount fromAccount.getCurrentAmount()) {throw new InsufficientAmountException(Insufficient Balance);} else {fromAccount.debit(amount);toAccount.credit(amount);}}}} 在上面的transferMoney方法中当2个线程A和B几乎同时尝试转移资金时可能会出现死锁。 A: transferMoney(acc1, acc2, 20);
B: transferMoney(acc2, acc1 ,25); 线程A可能已获得对acc1对象的锁定并正在等待获取对acc2对象的锁定同时线程B已获得了对acc2对象的锁定并且正在等待对acc1的锁定。 这将导致死锁并且必须重新启动系统 但是有一种避免这种情况的方法也就是所谓的“锁定排序”我个人认为这有点复杂。 ReentrantLock使用tryLock方法实现了一种更干净的方法。 这种方法称为“定时轮询轮询获取”。 如果您无法获取所有必需的锁释放已获取的锁并重试它可以让您重新获得控制权。 因此使用tryLock我们将尝试获取两个锁如果无法同时获取两个则释放它们如果已获取其中之一然后重试。 public boolean transferMoneyWithTryLock(Account fromAccount,Account toAccount, float amount) throws InsufficientAmountException, InterruptedException {// we are defining a stopTimelong stopTime System.nanoTime() 5000;while (true) {if (fromAccount.lock.tryLock()) {try {if (toAccount.lock.tryLock()) {try {if (amount fromAccount.getCurrentAmount()) {throw new InsufficientAmountException(Insufficient Balance);} else {fromAccount.debit(amount);toAccount.credit(amount);}} finally {toAccount.lock.unlock();}}} finally {fromAccount.lock.unlock();}}if(System.nanoTime() stopTime)return false;Thread.sleep(100);}//while} 在这里我们实现了定时锁因此如果在指定时间内无法获取锁则transferMoney方法将返回失败通知并正常退出。 我们还可以使用此概念来维护时间预算活动。 可中断锁获取 可中断的锁获取允许在可取消的活动中使用锁。 lockInterruptible方法使我们能够尝试获取锁但可用于中断。 所以基本上这意味着 它允许线程立即响应从另一个线程发送给它的中断信号。 当我们想向所有等待的锁发送KILL信号时这将很有帮助。 让我们看一个例子假设我们有一条共享的线来发送消息我们希望以这样的方式设计它如果另一个线程来了并中断了当前线程则该线程应释放锁并执行退出或关闭操作以取消当前任务。 public boolean sendOnSharedLine(String message) throws InterruptedException{lock.lockInterruptibly();try{return cancellableSendOnSharedLine(message);} finally {lock.unlock();}}private boolean cancellableSendOnSharedLine(String message){
....... 定时tryLock也可响应中断。 非块结构锁定 在固有锁中获取-释放对是块结构的即无论控制如何退出该锁始终在获取该锁的同一基本块中释放该锁。 外在锁提供了进行更明确控制的功能。 使用外部锁可以更轻松地实现某些概念例如“锁紧皮带”。 在哈希混和集合和链接列表中可以看到一些用例。 公平 ReentrantLock构造函数提供两种公平性选项供您选择创建非公平锁或公平锁。 公平锁的线程只能在它们所要求的顺序获取锁而一个不公平的锁允许锁获取它反过来的这就是所谓的驳运 打破了队列和获取锁当它变得可用。 由于挂起和恢复线程的开销公平锁定会带来巨大的性能成本。 在某些情况下恢复挂起的线程与实际运行之间会有明显的延迟。 让我们看一下情况 A - holds lock
B - has requested and is in suspended state waiting for A to release lock
C - requests the lock at the same time when A releases the lock, C has not yet gone to suspended state. 由于C尚未处于挂起状态因此它有可能获得A释放的锁使用它并在B甚至还没有唤醒之前释放它。 因此在这种情况下不公平锁定具有明显的性能优势。 内部锁和外部锁在内部具有相同的锁定机制因此性能的提高纯粹是主观的。 这取决于我们上面讨论的用例。 外部锁提供了更明确的控制机制可以更好地处理死锁饥饿等。我们将在以后的博客中看到更多示例。 参考 什么是可重入锁 从我们的JCG合作伙伴 Anirudh Bhatnagar在anirudh bhatnagar博客上获得。 翻译自: https://www.javacodegeeks.com/2013/11/what-are-reentrant-locks.html