<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 安装并启动 ...
随机推荐
- 求[1,n]中与m互素的个数
void dfs(int d, ll num, int z) { if (d>cnt) { if (num!=1) ans+=z*n/num; } else { dfs(d+1,num*p[d] ...
- Java编码 蛇形矩阵的构建与遍历输出
一.蛇形矩阵的构建,并按行输出 例: 输入:n, 生成n*n的蛇形矩阵 1 2 3 8 9 4 7 6 5 输出:1 2 3 8 9 4 7 6 5 java编码 public static void ...
- IntelliJ IDEA调试方法补充
基本用法&快捷键 1.首先说第一组按钮,共8个按钮,从左到右依次如下: > Show Execution Point (Alt + F10):如果你的光标在其它行或其它页面,点击这个按钮 ...
- Matlab-10:Ritz-Galerkin方法求解二阶常微分方程
一.代数多项式法: tic; clear clc % N=input('please key in the value of ''N'''); N=10; M=100; h=1/M; X=0:h:1; ...
- 【IDEA】【1】准备工作
1,官方网址(https://www.jetbrains.com/idea/) 2,网盘地址(为ideaIU-2018.2.5) 链接:https://pan.baidu.com/s/1oLgY_Pb ...
- 二元谓词中添加const的问题(未解决)
#include <iostream> using namespace std; #include"set" #include"algorithm" ...
- 【LeetCode】数值运算(除法、乘方)
C/C++数字范围(32位系统) ~ // 1 字节 char // 1 字节 ~ // 2 字节 - // 4 字节 unsigned: - // 4 字节 size_t: ~ // 4 字节 - ...
- 用javascript切换bootstrap的tab
html: <button class="tabContainer" data-toggle="tab" href="#note" i ...
- Hadoop---桥接集群的搭建
Hadoop---桥接群的搭建 集群分配:(一主四从) 我电脑的虚拟机:hadoop4:namenode+datanode+resourceManager 我one-friend的电脑虚拟机:hm ...
- WebSphere安装教程(WAS6.1为例)
1.网络准备 我们选择图形界面安装,如果堡垒机是windows则要在目标机器安装桌面环境并开启vcnserver:如果堡垒机是Linux则在堡垒机安装桌面环境和vncserver,然后将目标机的DIS ...