<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();
- }
- public interface Lock {
- 其中,
- 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 安装并启动 ...
随机推荐
- p2693 Combination Lock
深搜,注意模n.用set去重. #include <iostream> #include <cstdio> #include <cmath> #include &l ...
- p1211 Prime Cryptarithm
直接深搜+检验. #include <iostream> #include <cstdio> #include <cmath> #include <algor ...
- 04 flask 项目整体构建
本文主要的目标是创建flask基本的项目架构,总体架构: 详细的项目目录结构: Flask 项目创建的过程 一.项目(students)创建初始化工作 1. 创建项目的虚拟环境 mkvirtualen ...
- apicloud 环信总结
点击链接先查看一下apicloud 环信的文档 https://docs.apicloud.com/Client-API/Open-SDK/easeChat 文档中写了很多,但官方给的文档还是有问题, ...
- React文档(四)渲染元素
元素是React应用的最小单位. 一个React元素描述了你在屏幕上所看到的东西: const element = <h1>Hello, world</h1>; 和浏览器页面中 ...
- JQ 实现监测input中值的变化并绑定到另个input
$('#input').bind('input propertychange', function () { $('#myDiv ...
- Leetcode 1014. 在 D 天内送达包裹的能力
1014. 在 D 天内送达包裹的能力 显示英文描述 我的提交返回竞赛 用户通过次数197 用户尝试次数272 通过次数203 提交次数538 题目难度Medium 传送带上的包裹必须在 D 天 ...
- 2017-4-12/session
1. session的原理. <PHP核心技术与最佳实践>P299 session通过一个称为PHPSESSIONID的cookie和服务器联系.session通过sessionID判断客 ...
- C# 语法特性 - 泛型(C#2.0)
泛型将 类型参数 的概念引入了 .NET Framework. 泛型主要有两个优点: 1.编译时可以保证类型安全. 2.不用做类型转换,获得一定的性能提升. 泛型方法.泛型委托.泛型接口 除了泛型类之 ...
- Vue + webpack 项目配置化、接口请求统一管理
准备工作 需求由来: 当项目越来越大的时候提高项目运行编译速度.压缩代码体积.项目维护.bug修复......等等成为不得不考虑而且不得不做的问题. 又或者后面其他同事接手你的模块,或者改你的bug ...