博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JUC - ReentrantLock 的基本用法 以及 lock()、tryLock()、lockInterruptibly()的区别
阅读量:5319 次
发布时间:2019-06-14

本文共 4537 字,大约阅读时间需要 15 分钟。

ReentrantLock 与 synchronized对比

最近有在阅读Java并发编程实战这本书,又看到了ReentrantLock和synchronized的对比,发现自己以前对于RenntrantLock的理解很片面,特此做一番总结,如果有总结不到位的,欢迎指出

java.util.concurrent.locks 
接口 Lock

所有已知实现类:
,  , 

为什么需要Lock?

java.util.concurrent.locks 
接口 Lock

所有已知实现类:
,  , 

Lock提供了一种如条件的、可轮询的、定时的以及可以终端的获取锁的操作,所有的加锁方式和解锁方式都是显式的。

public class Lock {    private boolean locked = false;    public Lock() {    }    public final synchronized void lock() throws InterruptedException {        while(this.locked) {            this.wait();        }        this.locked = true;    }    public final synchronized void unlock() {        this.locked = false;        this.notifyAll();    }}

 

 Lock是JAVA5.0出现的,它的出现并不是为了替代synchronized,而是在synchronized不适用的时候使用。

那么synchronized有什么局限性呢?

  • 无法中断一个正在获取锁的线程

当一个线程想获取已经被其他线程持有的锁时,就会发生堵塞,假设已经持有锁的线程一直不释放锁,那么线程就会一直等待下去。

  •  无法指定获得锁的等待时间

比如,想要A线程执行某个操作,想在指定时间内A线程没有获取到锁就返回。synchronized是做不到的。

相同点:

  • 独占锁: 一次只允许一个线程访问
  • 可重入锁: 一个线程可重复获得自己已获得锁,不会发生死锁。简单来说,递归的时候不会发生死锁

不同点:

  • Lock不是java内置的,synchronized是JVM内置的,因此是内置特性。
  • 释放锁的方式:
    • Lock 必须要在finally中手动释放锁
    • synchronized 会根据锁区域代码自动执行完毕,或者发生异常,JVM会自动释放锁
  • 公平:
    • Lock是可公平可不公平锁
    • synchronized是不公平锁        

ReentrantLock的使用:

基本使用 

Lock lock = new ReentrantLock();        lock.lock();        try {            //....        } finally {            lock.unlock();        }

 

  • lock: 调用后一直阻塞直到获得锁。
package com.amber;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class TestReentrantLock {    Lock lock = new ReentrantLock();    public static void main(String[] args) {        TestReentrantLock testReentrantLock = new TestReentrantLock();        new Thread(() -> {            try {                testReentrantLock.testConcurrency(Thread.currentThread());            } catch (InterruptedException e) {                e.printStackTrace();            }        }, "线程1").start();        new Thread(() -> {            try {                testReentrantLock.testConcurrency(Thread.currentThread());            } catch (InterruptedException e) {                e.printStackTrace();            }        }, "线程2").start();    }    private void testConcurrency(Thread thread) throws InterruptedException {        //获取锁成功返回true,如果获取失败,等待2S,规定时间内还是没有获得锁,那么就返回false        if (lock.tryLock( 2000, TimeUnit.MICROSECONDS)) {            try {                System.out.println(thread.getName() + " :  " + "获取锁");                Thread.sleep(3000);            } finally {                System.out.println(thread.getName() + "释放锁");                lock.unlock(); //一定记得要释放锁            }        } else {            System.out.println(Thread.currentThread().getName() + " : " + "等待了,没有获取锁");        }    }}

 

  • tryLock:拿到锁返回true,否则false;带有时间限制的tryLock(long time, TimeUnit timeUnit),拿不到锁,就等待一段时间,超时返回false
  • lockInterruptibly :调用后如果没有获取到锁会一直阻塞,阻塞过程中会接受中断信号。
 lockInterruptibly有点难以理解,假设A线程想去获取锁,但是锁被B线程持有,那么A就会发生堵塞。 A堵塞的时候,可以有以下两种方法发生状态改变:
  1. A获取锁资源
  2. A被其他线程中断:
    1. 这里只得被其他线程中断的意思是,C线程调用A线程的interrupt()。那么此时A线程就会被唤醒,处理中断信号。

  lockInterruptibly是被中断,就由阻塞状态被唤醒去处理中断信号。

 在JAVA并发编程实战这本书中还提到了ReentrantLock的一个重要用法,那就是轮询锁。下面是书中的源代码:

1 public boolean transferMoney(Account fromAcct, Account toAcct, DollarAmount amount,  long timeout, TimeUnit unit) throws InsufficientFundsException, InterruptedException { 2     long fixedDelay = 1; 3     long randMod = 2; 4     long stopTime = System.nanoTime() + unit.toNanos(timeout); 5     while (true) { 6         if (fromAcct.lock.tryLock()) { 7             try { 8                 if (toAcct.lock.tryLock()) { //如果不能同事获得两个锁,那么线程就会释放已经获得的锁。这样可以很有效的解决死锁问题。 9                     try {10                         if (fromAcct.getBalance().compareTo(amount) < 0)11                             throw new InsufficientFundsException();12                         else{13                             fromAcct.debit(amount);14                             toAcct.credit(amount);15                             returntrue;16                         }17                     } finally {18                         toAcct.lock.unlock();19                     }20                 }21             } finally {22                 fromAcct.lock.unlock();23             }24         }25         if (System.nanoTime() < stopTime)26             returnfalse;27         NANOSECONDS.sleep(fixedDelay + rnd.nextLong() % randMod);28     }29 }

 

程序发生死锁的时候,往往只能通过重新启动程序解决。而有时候因为获取锁的时序不一致,很容易发生死锁。根据上述代码第6行和第8行,假设此时我们使用的synochronized内置锁,A线程从cc账号转账到dd账号,B线程从dd账号转账到cc账号,就很容易发生死锁。但是使用tryLock()却可以避免锁顺序造成死锁的问题,

如果线程A、B不能同时获取cc和dd对象的锁,那么就会放弃自己已经获得的锁。

转载于:https://www.cnblogs.com/amberbar/p/10296946.html

你可能感兴趣的文章
实用Android开发工具和资源精选
查看>>
数据持久化时的小bug
查看>>
bzoj2257
查看>>
http://www.bootcss.com/
查看>>
20145308 《网络对抗》 注入shellcode+Return-to-libc攻击 学习总结
查看>>
如何使用USBWebserver在本机快速建立网站测试环境
查看>>
变量提升
查看>>
jquery-jqzoom 插件 用例
查看>>
查看oracle数据库的连接数以及用户
查看>>
三.野指针和free
查看>>
python tkinter GUI绘制,以及点击更新显示图片
查看>>
Spring面试题
查看>>
C语言栈的实现
查看>>
SRM 628 DIV2
查看>>
2018-2019-2 20165314『网络对抗技术』Exp5:MSF基础应用
查看>>
SecureCRT的使用方法和技巧(详细使用教程)
查看>>
自建数据源(RSO2)、及数据源增强
查看>>
2018icpc徐州OnlineA Hard to prepare
查看>>
使用命令创建数据库和表
查看>>
linux下Rtree的安装
查看>>