<Java><Multi-thread><Lock interface>
Overview
- 介绍java的lock interface.
Motivation
- java拥有像synchronized这样的内置锁,那为什么还需要lock这样的外置锁呢?
- 首先,性能不是选择synchronized或lock的原因,因为jdk6中synchronized的性能已经和lock相差不大。
- 一般,选择lock是基于lock拥有的以下几个优点(内置锁不具备):
- 当获取锁时可以有一个等待时间,不会无期限等待下去;
- 当获取不到锁时,能够响应中断;[lockInterruptibly()]
- 可以在多读少写的应用场景中,提高性能; [new Condition()]
- 可以在获取不到锁时,立即返回false,获取到锁时返回true。[tryLock()]
Lock Interface
public interface Lock {
void lock(); // acquires the lock, 如果锁被其他线程获取,则进行等待 boolean tryLock(); // 尝试获取锁,成功则返回true,否则返回false void lockInterruptibly() throws InterruptedException; // 通过该方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。 boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition();
}- 其中,
- lockInterruptibly()表面加锁时,当前拥有锁的线程可以被中断;
- tryLock()则用于尝试获取锁,能获取则返回true,否则返回false;
- tryLock(long time, TimeUnit unit)与tryLock类似,只是会尝试一段时间;
- unlock()用于拥有锁的线程释放锁。
- newCondition()返回一个新的与当前实例绑定的Condition。
Continue that basic example
- 对于我们在上面讲的那个经典的生产者消费者的模型。我们已经知道为了防止deadlock,我们用notifyAll()取代了notify()。也就是每次会唤醒所有的存取线程,因为synchronized这种内置锁是加在类实例之上的,put()和take()共用一把锁。
- 很明显,为了优化这种方式,我们想每次唤醒的是状态能够发生变化的线程。比如说我put()之后就只想唤醒消费者线程,因为此时唤醒生产者线程是无意义的。
- 这种想法很明显只有通过将两种进程放到不同的等待队列,才能实现。
- 这就要用到上述Lock接口的newCondition()方法。
- 基于Lock interface的生产者消费者模型部分代码如下:
private final Lock lock = new ReentrantLock(); private final Condition notFullCondition = lock.newCondition();
private final Condition notEmptyCondition = lock.newCondition(); public void put(Object obj) throws InterruptedException{
lock.lock();
try {
while (count == DEFAULT_BUFFER_SIZE) {
System.out.println("the buffer is full,wait for a moment for putting ["+obj+"] to the buffer"+",thread:"+Thread.currentThread().getId());
notFullCondition.await(); // wait for the notFullCondition
}
if (tail >= DEFAULT_BUFFER_SIZE) tail = 0; buffer[tail] = obj;
count++;
System.out.println("success put the data ["+obj+"] to buffer,thread:"+Thread.currentThread().getId()); // then we invoke the thread in the notEmptyCondition wait queue
notEmptyCondition.signal();
} finally{
lock.unlock();
}
} public Object take() throws InterruptedException {
lock.lock();
Object res;
try {
while (count == EMPTY) {
System.out.println("the buffer is empty,just wait a moment,thread:"+Thread.currentThread().getId());
notEmptyCondition.await(); // wait for notEmptyCondition
}
res = buffer[header];
if (++header >= DEFAULT_BUFFER_SIZE) header = 0; count--;
if (count < EMPTY) count = 0; notFullCondition.signal(); // invoke the threads in the notFullCondition wait queue
} finally {
lock.unlock();
}
return res;
}- 上述代码中要注意:Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。所以一般来说,使用Lock必须在try catch块中进行,并且在finally块中释放锁,以保证锁一定被释放,防止deadlock的发生。
- ReentrantLock表示可重入锁,ReentrantLock是唯一实现了的Lock接口类。
ReentrantLock VS synchronized
Synchronized
- 把代码声明为synchronized,可以同时拥有原子性和可见性:
- 原子性:在同一时刻只有一个线程能对该段代码进行操作,从而防止在多个线程更新共享状态时互相冲突;
- 可见性:可见性需要对付内存缓存和编译器优化的各种反常行为(在多核处理器中,若多个线程对一个变量进行操作,这多个线程有可能被分配到多个处理器中运行,那么编译器会对代码进行优化:将变量复制一份到自己的片上存储器中,操作完后才赋值回主存,以此来提高速度。同样的,在单核处理器上也存在着由于“备份”造成的问题。)。一般来说,线程以某种不必让其他线程立即可以看到的方式。
ReentrantLock类
- 首先,ReentrantLock是一个类,implement Lock接口。通过使用类,而不是作为语言的特性来实现,这就为Lock的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。
- ReentrantLock在功能性方面更全面,比如时间锁等候,可中断锁等候,锁投票等。
<Java><Multi-thread><Lock interface>的更多相关文章
- 简单物联网:外网访问内网路由器下树莓派Flask服务器
最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...
- 利用ssh反向代理以及autossh实现从外网连接内网服务器
前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...
- 外网访问内网Docker容器
外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...
- 外网访问内网SpringBoot
外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...
- 外网访问内网Elasticsearch WEB
外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...
- 怎样从外网访问内网Rails
外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...
- 怎样从外网访问内网Memcached数据库
外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...
- 怎样从外网访问内网CouchDB数据库
外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...
- 怎样从外网访问内网DB2数据库
外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...
- 怎样从外网访问内网OpenLDAP数据库
外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...
随机推荐
- android -------- 常用依赖库
// bannercompile 'com.youth.banner:banner:1.4.9' // recyclecompile 'com.android.support:recyclervie ...
- mysql之filesort原理
在执行计划中,可能经常看到有Extra列有filesort,这就是使用了文件排序,这当然是不好的,应该优化,但是,了解一下他排序的原理也许很有帮助,下面看一下filesort的过程: 1.根据表的索引 ...
- HTML页面加载完毕后运行的js
Js方法:<script type=”text/javascript”> window.onload=function (){ var userName=”xiaoming”; alert ...
- Entertainment Box(可多执行的区间问题)
本题出自: Nordic Collegiate Programming Contest 2015 Ada, Bertrand and Charles often argue over which T ...
- css单位分析、颜色设置与调色板
CSS单位分析 px:单位代表像素,1px代表一个像素点. %:设置子元素为父容器的占比. em:代表该元素中一个字体所占字符,常用在文字首行缩进.其具有继承性. rem:始终代表html中的字符所在 ...
- leetcode-algorithms-30 Substring with Concatenation of All Words
leetcode-algorithms-30 Substring with Concatenation of All Words You are given a string, s, and a li ...
- iphone手机在网页返回上一页时,部分字体变大问题
最近做一个项目是,发现了一个iphone的兼容性问题,在返回上一页后,部分字体会变大,刷新就会正常. 经过总结,发现都是span标签里面的字体变大.经过查询发现,需要给span添加一个属性:displ ...
- SQL 经典回顾:JOIN 表连接操作不完全指南
2017-02-23 小峰 ITPUB 点击上方“蓝字”可以关注我们哦  |转载自:码农网 |原文链接:www.codeceo.com/article/sql-join-guide.html ...
- UI基础一:简单的BOL查询
利用标准的BOL编辑工具,添加BOL对象,重写查询方法,实现简答的BOL查询 1.SE11创建查询对象结构: 2.SE11创建查询结果对象: 3.SE24新建处理类: 重写查询结果方法: METHOD ...
- body当中的属性
1. text ——文本颜色 <body text="green"> </body> 2. link ——超链接的颜色 ...