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框架用于并行执行任务,核心的思想就是将一个大任务切分成多个小任务,然后汇总每个小任务 ...
随机推荐
- C++ mfc 简易文本编辑器 遇到的一些问题
[题目40]简易文本编辑器. 设计一个简易的文本编辑器. 设计要求: (1) 具有图形菜单界面: (2) 查找,替换(等长,不等长),插入(插串,文本块的插入).文本块移动(行块,列块移动),删除; ...
- printf的封装与实现
1 UART通信协议 1.1 UART通信的物理连接 图1 UART的物理连接 1.2 逻辑电平 用电平表示逻辑1和逻辑0,逻辑1和逻辑0用来组织计算机层面的数据. 1.3 电平标准 根据通讯使用的电 ...
- HttpClient测试
导入maven依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson&l ...
- python三大神器===》装饰器
1.认识装饰器 如果你经常看我的博客,你已经学会了python的前两大‘神器’(迭代器,生成器),那么什么是装饰器呢?就如字面意义装饰器是对某个事物(通常指函数)进行装饰,让其在不修改任何内部代码的情 ...
- Fr3设置图片打印
见 fr3的文件内容,为xml <?xml version="1.0" encoding="utf-8"?> <TfrxReport Vers ...
- SQL decode 函数的用法
decode 函数基本语法: decode(字段|表达式,条件1,结果1,条件2,结果2,...,条件n,结果n,缺省值): --缺省值可以省略 表示如果 字段|表达式 等于 条件1 时,DECODE ...
- office组件导入导出常见异常记录
异常:未能加载文件或程序集"Microsoft.Office.Interop.Excel, Version=11.0.0.0, Culture=neutral, PublicKeyToken ...
- JAVA程序中常用概念介绍
一.关键字.引用.直接量.变量.长量概念 1.关键字 java内部定义的java语言专用的单词,这些单词具有特殊含义,开发人员在定义自己声明的名称时,应该避开这些专用的单词.这些专用的单词也就称之为j ...
- Spring框架模块
Spring 框架介绍 Spring 框架模块 Spring开发环境搭建(Eclipse) 创建一个简单的Spring应用 Spring 控制反转容器(Inversion of Control – I ...
- 文件的概念、标准IO其一
1.文件的概念 文件是一种存储在磁盘(掉电不丢失存储设备)上,掉电不丢失的一种存储数据的方式,文件在系统中有以下层次的结构来实现. 系统调用.文件IO.标准IO的关系如下: 2.linux系统的文件分 ...