java concurrent lock_1. Lock - java.util.concurrent.lock

首先我们先介绍最常用的锁。锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源(不过有些锁可以允许多个线程并发访问共享资源,比如读写锁、信号量等)。在Lock接口出现之前,Java程序是靠synchronized关键字实现锁功能的,在Java SE 5后,并发包新增了Lock接口以及相关实现类用来实现锁功能,它提供了与synchronized关键字类似的同步功能,只是使用时需要显式的获取和释放锁。虽然它缺少了synchronized隐式获取释放锁的便捷性,但是拥有了锁获取与释放的可操作性、可中断的获取锁以及超时获取锁等多种synchronized关键字不具备的同步特性。

使用synchronized关键字将会隐式获取锁,但是它将锁的获取释放固化了。虽然这种方式简化了同步的管理,但是扩展性变差了。例如,针对一个场景,先获取锁A,再获取锁B,获取锁B后释放锁A然后获取锁C,获取锁C后释放锁B再获取锁D,此时使用synchronized关键字就很难实现了,而使用Lock却容易很多。

Lock的使用如下:

Lock lock = new ReentrantLock();

lock.lock();

try {

//do something

} finally {

lock.unlock();

}

在finally块释放锁可以保证如果try块的逻辑代码出现异常后,锁能够保证被释放,避免出现死锁等其他并发问题。同时,不要将获取锁的过程写在try块中,如果锁获取过程出现异常,最后会导致锁无故释放。

Lock接口提供的synchronized关键字不具备的特性如下:

特性

描述

尝试非阻塞的获取锁

当前线程尝试获取锁,不管是否成功立即返回

获取锁能被中断

与synchronized关键字不同,获取锁的线程能够响应中断,当获取锁的线程被中断时,中断异常会被立刻抛出

超时获取锁

在指定的截止时间前获取锁,如果截止时间到了仍然未获取到锁,返回

下面是锁的三个特性的示例:

public class LockTest {

private Lock lock = new ReentrantLock();

@Test

public void unblockTest() {

Thread thread = new Thread(() -> {

lock.lock();

try {

System.out.println(Thread.currentThread().getName() + " acquire lock");

SleepUtils.sleep(2);

} finally {

lock.unlock();

}

});

thread.start();

SleepUtils.sleep(1); //等待thread获取到锁

if(lock.tryLock()) { //尝试非阻塞获取锁

//do something

System.out.println(Thread.currentThread().getName() + " acquire lock");

} else {

//do something

System.out.println(Thread.currentThread().getName() + " acquire lock fail");

}

}

@Test

public void interruptedTest() {

Thread thread = new Thread(() -> {

lock.lock();

try {

System.out.println("Thread-1 acquire lock");

SleepUtils.sleep(3);

} finally {

lock.unlock();

System.out.println("Thread-1 release lock");

}

});

thread.start();

Thread thread2 = new Thread(() -> {

boolean interrupted = false;

try {

lock.lockInterruptibly();

} catch (InterruptedException e) {

//当获取锁时被中断,可以做其他的事情

System.out.println("Thread2 is interrupted");

interrupted = true;

}

if(!interrupted) {

try {

System.out.println("Thread-2 acquire lock");

SleepUtils.sleep(1);

} finally {

lock.unlock();

System.out.println("Thread-2 release lock");

}

}

});

SleepUtils.sleep(1); //等待线程1获取锁

thread2.start();

SleepUtils.sleep(1); //等待线程2处于获取锁的过程中

thread2.interrupt(); //中断线程2

SleepUtils.sleep(2);

}

@Test

public void timeoutTest() throws InterruptedException {

Thread thread = new Thread(() -> {

lock.lock();

try {

System.out.println(Thread.currentThread().getName() + " acquire lock");

SleepUtils.sleep(2);

} finally {

lock.unlock();

}

});

thread.start();

SleepUtils.sleep(1); //等待thread获取到锁

if(lock.tryLock(1, TimeUnit.SECONDS)) { //超时获取锁

//do something

System.out.println(Thread.currentThread().getName() + " acquire lock");

} else {

//do something

System.out.println(Thread.currentThread().getName() + " acquire lock time out");

}

}

}

输出如下:

Thread-0 acquire lock

main acquire lock fail

Thread-1 acquire lock

Thread2 is interrupted

Thread-1 release lock

Thread-0 acquire lock

main acquire lock time out

以上三种方式能够向开发者提供一种更灵活的方式处理锁获取,而不是只在无法获取到锁时一直等待。

API

Lock接口的基本API如下:

方法名称

描述

void lock()

获取锁,调用该方法后当前线程会获取锁,只有锁获得后才会返回

void lockInterruptibly() throws InterruptedException

可中断的获取锁,和lock()的不同之处在于该方法会响应中断,即在锁的获取过程中可以中断该线程

boolean tryLock()

尝试非阻塞的获取锁,调用该方法立刻返回,如果获取成功返回true,否则返回false

boolean tryLock(long time, TimeUnit unit) throws InterruptedException

超时获取锁,在以下情况下会返回:

- 当前线程成功获取锁

- 其他线程中断了当前线程

- 指定的等待时间结束

void unlock()

释放锁

Condition newCondition()

获取等待通知组件,该组件和当前锁绑定,当前线程只有获取了锁,才能调用该组件的wait()方法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值