Java中的Lock接口
Synchronized & Lock
synchronized 是Java语言中的关键字,由monitorenter,monitorexit两个指令实现。JVM会将monitorenter指定插在同步代码块开始的地方,将monitorexit指定插在同步代码快结束和出现异常的地方。
Lock是JUC包下的组件, 是基于AQS(队列同步器)实现的。
synchronized功能与ReentrantLock类相对应, 都是可重入的锁。
Lock与synchronized关键字相比,实现了公平锁和非公平锁,synchronized关键字是非公平的。同时Lock接口提供了在获取锁被阻塞时可响应中断以及超时获取锁的API。
Lock接口可以绑定多个条件,即绑定多个 Condition 对象,这样唤醒时可以唤醒指定条件上的线程。如生产者消费者例子,使用 synchronized 关键字时,当生产者生产消息时,需要唤醒消费者线程,我们只能调用notifyAll 方法,这样不仅会唤醒消费者端的线程而且还会唤醒生产者自己这一端的线程。
**注: **
- 公平锁是指多个线程等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁,而非公平锁不保证这一点。在锁被释放时,任何一个等待锁的线程都有机会获取锁。
- ReentrantLock实现的非公平锁只能保证阻塞队列里最早等待的线程与新来的线程竞争抢锁, 对于阻塞队列其它的线程依然需要等待,因为队列是先进先出的,只有该线程获取锁然后释放锁后后续节点才有资格竞争锁。
Lock 接口的 API
public interface Lock {
// 获取锁直到获取锁成功后返回,否则被阻塞,即使被中断也不会返回
void lock();
// 获取锁并且响应中断,注意如果t线程因为竞争锁失败而被阻塞,另外一个线程中断了t线程
// 那么t线程会被唤醒,并且抛出中断异常,清除中断状态, 阻塞线程的方法是调用LockSupport.park()
// 当调用了LockSupport.unpark或者中断了线程,线程会从park方法中唤醒。
void lockInterruptibly() throws InterruptedException;
// 尝试获取锁,不管成功与否都返回
boolean tryLock();
// 超时获取锁,此方法返回的情况如下:
// 1. 在给定时间内竞争到锁, 返回true
// 2. 超时时间过了, 返回false
// 3. 线程被中断,抛出中断异常,清除中断状态
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 释放锁,如果当前线程没有获取到锁,调用此方法将会抛出IllegalMonitorStateException
void unlock();
// 创建Condition对象,提供了类似synchronized 锁对象的wait, notify, notifyAll方法
Condition newCondition();
}
public interface Condition {
// 使获取锁的线程阻塞,并且释放锁,直到另一个线程中断了此线程或者调用了signal,signalAll方法
// 没有获取锁的线程调用此方法会抛出IllegalMonitorStateException
void await() throws InterruptedException;
// 使获取的线程阻塞,并且释放锁,但是不响应中断请求,只有调用了signal,signalAll才会被唤醒
// 其实是线程中断后醒过来,再次被阻塞-LockSupport.park()
void awaitUninterruptibly();
// 使获取的线程阻塞,并且释放锁, 被唤醒的原因如下:
// 1. 超时时间已过 2. 线程被中断 3. 有线程调用了signal,signalAll方法
boolean await(long time, TimeUnit unit) throws InterruptedException;
// 唤醒最开始等待在条件队列上的线程
void signal();
// 唤醒所有等待在条件队列上的线程
void signalAll();
}
注: 所有从await中醒过来的线程只有重新获取到锁才能往下执行,否则依然会在同步队列中等待获取锁
Lock的使用
下面将使用ReentrantLock实现一个简单的阻塞队列。
public class BlockQueue {
private Lock lock = new ReentrantLock();
private Condition full = lock.newCondition();
private Condition empty = lock.newCondition();
private Queue<String> queue;
private int capacity;
public BlockQueue(int capacity) {
queue = new ArrayDeque<>(capacity);
this.capacity = capacity;
}
public void put(String element) {
lock.lock();
try {
while (queue.size() == capacity) {
try {
full.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.add(element);
empty.signalAll();
} finally {
lock.unlock();
}
}
public String take() {
lock.lock();
try {
while (queue.isEmpty()) {
try {
empty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String element = queue.remove();
full.signalAll();
return element;
} finally {
lock.unlock();
}
}
}
测试Lock接口的方法
public class LockTest {
private final Object lock = new Object();
/**
* 测试synchronized关键字
* synchronized: 当线程在获取锁时, 竞争失败的线程会处于阻塞, 并且不响应中断直至获取到锁
*
* 程序结果:
* --------------主线程准备释放锁---------------------
* 是否被中断: true
* block-thread获取锁成功
*/
@Test
public void testSynchronized() {
// 当前线程持有锁
synchronized (lock) {
// 开启另外一个线程尝试获取锁
Thread t = new Thread(()->{
//阻塞
synchronized (lock) {
System.out.println("是否被中断: " + Thread.currentThread()
.isInterrupted());
System.out.println(Thread.currentThread().getName() + "获取锁成功");
}
}, "block-thread");
t.start();
//休眠一秒, 让开启的线程充分运行, 接着进行中断
sleep(1);
t.interrupt();
//长久睡眠不释放锁, 用来观察结果, 看看线程t是否会响应中断
sleep(10);
System.out.println("--------------主线程准备释放锁---------------------");
}
}
/**
* 测试lock.lock()同synchronized关键字
*
* 程序结果:
* --------------主线程准备释放锁---------------------
* 是否被中断: true
* block-thread获取锁成功
*/
@Test
public void testLock() {
//当前线程持有锁
Lock lock = new ReentrantLock();
lock.lock();
try {
// 开启另外一个线程尝试获取锁
Thread t = new Thread(()->{
//阻塞
lock.lock();
try {
System.out.println("是否被中断: " + Thread.currentThread()
.isInterrupted());
System.out.println(Thread.currentThread().getName() + "获取锁成功");
} finally {
lock.unlock();
}
}, "block-thread");
t.start();
//休眠一秒, 让开启的线程充分运行, 接着进行中断
sleep(1);
t.interrupt();
//长久睡眠不释放锁, 用来观察结果, 看看线程t是否会响应中断
sleep(10);
System.out.println("--------------主线程准备释放锁---------------------");
} finally {
lock.unlock();
}
}
/**
* 测试lock.lockInterruptibly(): 抛出中断异常
* 当线程被中断后,线程会从LockSupport.park()中醒过来,然后会检查自己是否被中断,如果被中断过
* 则抛出中断异常,清除中断状态。
*
* 程序结果: block-thread is interrupted! exit
*/
@Test
public void testlockInterruptibly() {
// 当前线程持有锁
ReentrantLock lock = new ReentrantLock();
try {
lock.lock();
// 开启另外一个线程尝试获取锁
Thread t = new Thread(()->{
//阻塞
try {
lock.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + "获取锁成功");
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() +
"is interrupted! exit");
} finally {
if(lock.isHeldByCurrentThread())
lock.unlock();
}
}, "block-thread");
t.start();
//休眠一秒, 让开启的线程充分运行, 接着进行中断
sleep(1);
t.interrupt();
//长久睡眠不释放锁, 用来观察结果, 看看线程t是否会相应中断
sleep(10);
} finally {
lock.unlock();
}
}
private void sleep(int second) {
try {
TimeUnit.SECONDS.sleep(second);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Java中的Lock接口的更多相关文章
- Java中的Lock与synchronized
并发编程学习笔记之Lock与synchronized 一.什么是可重入锁 Lcok在Java中是一个接口,一般在面试问题中问到的可能是ReentrantLock与synchronized的区别.Ree ...
- Java 中的集合接口——List、Set、Map
Java 中的集合接口——List.Set.Map 什么叫集合:集合就是Java API所提供的一系列类的实例,可以用于动态存放多个对象.这跟我们学过的数组差不多,那为什么我们还要学集合,我们看看数组 ...
- 转:二十一、详细解析Java中抽象类和接口的区别
转:二十一.详细解析Java中抽象类和接口的区别 http://blog.csdn.net/liujun13579/article/details/7737670 在Java语言中, abstract ...
- 关于JAVA中抽象类和接口的区别辨析
今天主要整理一下新学习的有关于Java中抽象类和接口的相关知识和个人理解. 1 抽象类 用来描述事物的一般状态和行为,然后在其子类中去实现这些状态和行为.也就是说,抽象类中的方法,需要在子类中进行重写 ...
- java中的标记接口(标签接口)
Java中的标记接口(Marker Interface),又称标签接口(Tag Interface),具体是不包含任何方法的接口.在Java中很容易找到标记接口的例子,比如JDK中的Serialzab ...
- Java中的Serializable接口和transient关键字
Java中的Serializable接口和transient关键字 Table of Contents 1. 向memcached中放数据时遇到NotSerializableException异常 2 ...
- 一文带你看懂Java中的Lock锁底层AQS到底是如何实现的
前言 相信大家对Java中的Lock锁应该不会陌生,比如ReentrantLock,锁主要是用来解决解决多线程运行访问共享资源时的线程安全问题.那你是不是很好奇,这些Lock锁api是如何实现的呢?本 ...
- 用好JAVA中的函数式接口,轻松从通用代码框架中剥离掉业务定制逻辑
大家好,又见面了. 今天我们一起聊一聊JAVA中的函数式接口.那我们首先要知道啥是函数式接口.它和JAVA中普通的接口有啥区别?其实函数式接口也是一个Interface类,是一种比较特殊的接口类,这个 ...
- Java Concurrency API 中的 Lock 接口(Lock interface) 是什么?对比同步它有什么优势?
Lock 接口比同步方法和同步块提供了更具扩展性的锁操作. 他们允许更灵活的结构,可以具有完全不同的性质,并且可以支持多个相关类的 条件对象. 它的优势有: 可以使锁更公平 可以使线程在等待锁的时候响 ...
随机推荐
- java.util.logging jdk日志详解
jdk自带的日志,结构并不复杂,功能也能满足绝大部分功能.日志写入位置是开放的,只要继承了handler都可以接收日志的写入.handler本身依赖于LogRecord对象,该对象代表一个日志.Han ...
- Unity for VsCode
安装以下两个插件 以下设置VsCode在换行保存时不删除tab空格
- MYSQL性能优化(3)
优化数据库对象 1.优化表的数据类型 select * from tbl1 procedure analyse(16,256) ,会输出优化建议,结合情况优化 2.拆分表(仅Myisam) 2.1 纵 ...
- python入门(七):字符串
1.字符串类型: >>> s="早上好" #str类型的字符串 >>> type(s) <class 'str ...
- CORSFilter 跨域资源访问
CORS 定义 Cross-Origin Resource Sharing(CORS)跨来源资源共享是一份浏览器技术的规范,提供了 Web 服务从不同域传来沙盒脚本的方法,以避开浏览器的同源策略,是 ...
- yum方面的知识
修改CentOS默认yum源为国内yum镜像源 1.mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bac ...
- 算法练习LeetCode初级算法之树
二叉树的前序遍历 我的解法:利用递归,自底向下逐步添加到list,返回最终的前序遍历list class Solution { public List<Integer> preorderT ...
- java基础 ---- 练习for循环
----- 使用for循环打印图形 //打印矩形 public class Print { public static void main(String[] args) { for(int i=1 ...
- stark组件开发之排序
class StartHandler(object): .......... ordered_list = [] # 排序规则由 用户指定. def get_ordered_list(self): r ...
- 如何在chrome上打开SSL3.0
Chrome默认关闭对SSL3.0的支持,无法访问一些Web应用.可以手动打开他. 启动chrome依次选择 设置->高级->系统->打开代理设置->安全 将使用SSL 3.0 ...