Java并发分析—Lock
1.Lock 和 Condition
当使用synchronied进行同步时,可以在同步代码块中只用常用的wait和notify等方法,在使用显示锁的时候,将通过Condition对象与任意Lock实现组合使用,为每个对象提供多个等待方法,其中Lock代替了synchronized方法和语句的使用,Condition代替了Object监视器方法的使用,条件Condition为线程提供了一个含义,以便在某个状态条出现可能为true,另一个线程通知它之前,一直挂起该线程,即让其等待,因为访问该共享状态信息发生在不同的线程中,所以它必须受到保护。
2.Lock 和 ReentrantLock
Lock 接口定义了一组抽象的锁定操作。与内部锁定(intrinsic locking)不同,Lock 提供了无条件的、可轮询的、定时的、可中断的锁获取操作,所有加锁和解锁的方法都是显式的。这提供了更加灵活的加锁机制,弥补了内部锁在功能上的一些局限——不能中断那些正在等待获取锁的线程,并且在请求锁失败的情况下,必须无限等待。
Lock 接口主要定义了下面的一些方法,并通过ReentrantLock实现Lock 接口:
package com.test; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock; public class LockTest implements Lock{ /**
* lock()用来获取锁。如果锁已被其他线程获取,则进行等待
*/
public void lock() {} /**
* 通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,
* 那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
*/
public void lockInterruptibly() throws InterruptedException {} /**
* 表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,
* 这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待
*/
public boolean tryLock() {
return false;
} /**
* 这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。
* 如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true
*/
public boolean tryLock(long time, TimeUnit unit)
throws InterruptedException {
return false;
} /**
* 释放锁,必须在finally中释放
*/
public void unlock() {} public Condition newCondition() {
return null;
}
}
(1)void lock():获取锁。如果锁不可用,出于线程调度目的,将禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态。
例:
package com.test; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class Main {
Lock lock = new ReentrantLock();
private int i = 0; public static void main(String[] args) {
final Main main = new Main();
new Thread(new Runnable() {
public void run() {
main.write(Thread.currentThread());
}
}).start();
new Thread(new Runnable() {
public void run() {
main.write(Thread.currentThread());
}
}).start();
} public void write(Thread thread) {
lock.lock();
try {
System.out.println(thread.getName() + "获取了锁");
i = 1;
} catch (Exception e) {
} finally {
lock.unlock();
System.out.println(thread.getName() + "释放了锁");
}
}
}
运行结果:
Thread-0获取了锁
Thread-0释放了锁
Thread-1获取了锁
Thread-1释放了锁
(2)void lockInterruptibly() throws InterruptedException:如果当前线程未被中断,则获取锁。如果锁可用,则获取锁,并立即返回。如果当前线程在获取锁时被 中断,并且支持对锁获取的中断,则将抛出InterruptedException,并清除当前线程的已中断状态。
中断线程的方法参照: https://www.cnblogs.com/jenkov/p/juc_interrupt.html
例:
package com.test; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class InterruptionInJava extends Thread {
Lock lock = new ReentrantLock();
private volatile static boolean on = false; public static void main(String[] args) throws InterruptedException {
Thread testThread = new Thread(new InterruptionInJava(), "t1");
Thread testThread1 = new Thread(new InterruptionInJava(), "t2");
testThread.start();
testThread1.start();
Thread.sleep(1000);
InterruptionInJava.on = true;
testThread.interrupt();
} @Override
public void run() {
try {
test(Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
} public void test(Thread thread) throws InterruptedException {
lock.lockInterruptibly();
try {
System.out.println(thread.getName() + "获取了锁");
while (!on) {
try {
Thread.sleep(10000000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()
+ "被中断了");
}
}
} finally {
lock.unlock();
System.out.println(thread.getName() + "释放了锁");
}
}
}
运行结果:
t1获取了锁
t2获取了锁
t1被中断了
t1释放了锁
(3)boolean tryLock():如果锁可用,则获取锁,并立即返回值 true。如果锁不可用,则此方法将立即返回值 false。
tryLolck()还能够实现可轮询查询,如果不能获得所有需要的锁,则可以使用轮询的获取方式重新获取控制权,它会释放已经获得的控制权,然后重新尝试。
例:
package com.test; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class Main {
Lock lock = new ReentrantLock();
private int i = 0; public static void main(String[] args) {
final Main main = new Main();
new Thread(new Runnable() {
public void run() {
try {
main.write(Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
try {
main.write(Thread.currentThread());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"线程被中断了");
}
}
}).start();
} public void write(Thread thread) throws InterruptedException{
if(lock.tryLock()) {
try {
System.out.println(thread.getName() + "获取了锁");
Thread.sleep(5000);
} finally {
lock.unlock();
System.out.println(thread.getName() + "释放了锁");
}
}else {
System.out.println(thread.getName()+"当前锁不可用");
}
}
}
运行结果:
Thread-0获取了锁
Thread-1当前锁不可用
Thread-0释放了锁
(4)boolean tryLock(long time, TimeUnitunit) throws InterruptedException:如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁。
当使用内部锁时,一旦开始请求,锁就不能停止,所以内部锁实现具有时限的活动带来了风险,为了解决这一问题,可使用定时锁,当具有时限的活动调用阻塞方法,定时锁能够在时间预算内设定相应的超时,如果活动在期待的时间内没能获得结果,定时锁能使程序提前返回,可定时锁由boolean tryLock(long time, TimeUnitunit)实现。
例:
package com.test; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class Main {
Lock lock = new ReentrantLock();
private int i = 0; public static void main(String[] args) {
final Main main = new Main();
new Thread(new Runnable() {
public void run() {
try {
main.write(Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
try {
main.write(Thread.currentThread());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"线程被中断了");
}
}
}).start();
} public void write(Thread thread) throws InterruptedException{
if(lock.tryLock(2000, TimeUnit.MILLISECONDS )) {
try {
System.out.println(thread.getName() + "获取了锁");
Thread.sleep(1500);
} finally {
lock.unlock();
System.out.println(thread.getName() + "释放了锁");
}
}else {
System.out.println(thread.getName()+"当前锁不可用");
}
}
}
运行结果:
Thread-1获取了锁
Thread-1释放了锁
Thread-0获取了锁
Thread-0释放了锁
(5)void unlock():释放锁。
例:把上例释放锁代码屏蔽掉
package com.test; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class Main {
Lock lock = new ReentrantLock();
private int i = 0; public static void main(String[] args) {
final Main main = new Main();
new Thread(new Runnable() {
public void run() {
try {
main.write(Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
try {
main.write(Thread.currentThread());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"线程被中断了");
}
}
}).start();
} public void write(Thread thread) throws InterruptedException{
if(lock.tryLock(2000, TimeUnit.MILLISECONDS )) {
try {
System.out.println(thread.getName() + "获取了锁");
Thread.sleep(1500);
} finally {
//lock.unlock();
//System.out.println(thread.getName() + "释放了锁"); }
}else {
System.out.println(thread.getName()+"当前锁不可用");
}
}
}
运行结果:
Thread-1获取了锁
Thread-0当前锁不可用
由于线程1没有释放锁,线程2在获取锁时时得不到锁的。
(6)Condition newCondition():返回绑定到此 Lock 实例的新 Condition 实
除以上方法外,ReentrantLock 还增加了一些高级功能,主要有以下3项:
(1)等待可中断:当持有锁的线程长期不释放锁时,正在等待的线程可以选择放弃等待,改为处理其他事情,可中断特性对处理执行时间非常长的同步块很有帮助。
(2)公平锁:多个线程在等待同一个锁时必须按照申请锁的时间顺序来依次获得锁,而非公平锁不能保证这一点,在锁被释放时,任何一个等待锁的线程都有机会获得锁,synchronized中的锁时非公平的,ReentranLock默认情况下也是非公平的,但可以通过带布尔值的构造函数要求使用公平锁。
(3)锁绑定多个条件:指一个ReentrantLock对象可以同时绑定多个Condition对象,er在synchronized中,锁对象的wait()和notify()或notifyAll()方法可以实现一个隐含的条件,如果和多余一个的条件关联的时候,就不得不额外添加一个锁,而ReentrantLock则无需这样做,只需要多次调用newCondition()方法即可。
例,调用 Condition.await() 将在等待前以原子方式释放锁,并在等待返回前重新获取锁。
ReentrantLock 实现了Lock 接口。获得ReentrantLock 的锁与进入synchronized块具有相同的语义,释放 ReentrantLock 锁与退出synchronized 块有相同的语义。相比于 synchronized,ReentrantLock 提供了更多的灵活性来处理不可用的锁。
3.编程时锁的选择
1.最好既不是用 Lock/Condition 也不使用 synchronized关键字,在许多情况下,可以使用java.util.concurrent包中的一种机制,它会处理所有的加锁。
2.如果synchronized 关键字适合编写的程序,那就尽量使用它,这样可以减少编写的代码数量,减少出错的几率,如果特别需要Lock/Condition 结构提供的独有的特性时,才是用Lock/Condition 。
参考文献:
1. https://www.cnblogs.com/liuconglin/p/6693825.html#_label1_3
2.https://www.cnblogs.com/kylindai/archive/2006/01/24/322667.html
3.不明来历pdf文档《Java锁机制详解》,如有侵权,请联系LZ。
Java并发分析—Lock的更多相关文章
- Java 并发:Lock 框架详解
摘要: 我们已经知道,synchronized 是java的关键字,是Java的内置特性,在JVM层面实现了对临界资源的同步互斥访问,但 synchronized 粒度有些大,在处理实际问题时存在诸多 ...
- java并发库 Lock 公平锁和非公平锁
jdk1.5并发包中ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,关于两者区别,java并发编程实践里面有解释 公平锁: Threads acquir ...
- Java并发编程:Lock
Java并发编程:Lock 在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.l ...
- Java并发分析—ConcurrentHashMap
LZ在 https://www.cnblogs.com/xyzyj/p/6696545.html 中简单介绍了List和Map中的常用集合,唯独没有CurrentHashMap.原因是CurrentH ...
- Java并发分析—volatile
在https://www.cnblogs.com/xyzyj/p/11148497.html中已经说明了在多线程并发的情况下,会出现数据的不一致问题,但归根结底就是一个原因,在宏观上就是线程的执行顺序 ...
- Java并发分析—synchronized
在计算机操作系统中,并发在宏观上是指在同一时间段内,同时有多道程序在运行. 一个程序可以对应一个进程或多个进程,进程有独立的存储空间.一个进程包含一个或多个线程.线程堆空间是共享的,栈空间是私有的.同 ...
- Java并发基础--Lock的学习
一.Lock的出现 Lock的主要作用实现线程之间的同步互斥,与synchronized关键字的效果是一样的,synchronized是Java语言内置的特性,那么为什么又出现了Lock呢?原因是sy ...
- java 并发编程lock使用详解
浅谈Synchronized: synchronized是Java的一个关键字,也就是Java语言内置的特性,如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,执行代码块时,其 ...
- Java并发编程(07):Fork/Join框架机制详解
本文源码:GitHub·点这里 || GitEE·点这里 一.Fork/Join框架 Java提供Fork/Join框架用于并行执行任务,核心的思想就是将一个大任务切分成多个小任务,然后汇总每个小任务 ...
随机推荐
- CentOS 6.5(x86_32)下安装Oracle 10g R2
一.硬件要求 1.内存 & swap Minimum: 1 GB of RAMRecommended: 2 GB of RAM or more 检查内存情况 # grep MemTotal / ...
- 项目中常用的JS操作技巧
1.<a>标签-超链接中confirm方法使用介绍 <a href="a.html" onclick="if(confirm('确定删除?')==fal ...
- netty权威指南学习笔记五——分隔符和定长解码器的应用
TCP以流的方式进行数据传输,上层应用协议为了对消息进行区分,通常采用以下4中方式: 消息长度固定,累计读取到长度综合为定长LEN的报文后,就认为读取到了一个完整的消息,将计数器置位,重新开始读取下一 ...
- java se
集群 数据库集群.服务器集群.内存 java特性 封装:封装细节和封装变化(可能发生需求变更的代码必须要封装,set方法除了完成赋值功能外,还能处理额外的任务,记录访问的人) 继承 多态 访问限定符 ...
- ActiveMQ持久化机制和JMS可靠消息
1.ActiveMQ持久化机制 1.1 JDBC将数据持久化到数据库 1.2 AMQ生成日志文件 1.3 KahaDB:本次磁盘生成数据文件(默认) 1.4 LevelDB:谷歌K/V数据库 1.5 ...
- 八、React实战:可交互待办事务表(表单使用、数据的本地缓存local srtorage、生命同期函数(页面加载就会执行函数名固定为componentDidMount()))
一.项目功能概述 示例网址:http://www.todolist.cn/ 功能: 输入待做事项,回车,把任务添加到 [正在进行] [正在进行] 任务,勾选之后,变成已[经完成事项] [已完成事务], ...
- robotframework+selenium2Library怎样不用手动关掉代理
每次跑脚本,启动浏览器都要手动关掉代理,太费劲了,发现最简单的办法就是在局域网设置里面勾上跳过本地地址的代理服务器,并且在例外里写上127.0.0.1 就这么简单有没有??
- ios系统web(微信公众号)开发遇到的问题及解决方案
1.1. 页面滚动不流畅(2017-09-25) 现象: 网页竖向滚动或横向滚动不流畅. 解决方案: 为滚动元素添加css样式: -webkit-overflow-scrolling: touch; ...
- MySQL日志--二进制日志
(一)什么是二进制日志 二进制日志(binlog)记录了数据库中所有的DDL和DML操作,但是不包括select语句,语句以"事件"的形式保存,记录了数据库的更改变化,在主从复制( ...
- python阴阳鱼绘制(使用turtle)
from turtle import * def draw(radius ,color1 , color2): #设置画笔的大小 width(3) #设置画笔颜色和填充颜色 color("b ...