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框架用于并行执行任务,核心的思想就是将一个大任务切分成多个小任务,然后汇总每个小任务 ...
随机推荐
- Java 类加载器(ClassLoader)
类加载器 ClassLoader 什么是类加载器? 通过一个类的全限定名来获取描述此类的二进制字节流这个动作放到Java虚拟机外部去实现, 以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代 ...
- 创建一个TCP代理
代理技术 代理一般被我们用于"穿墙",具体来说可以看这里,或者查wiki.理解代理背后的原理之后,便很容易知道,代理的作用不仅仅只是"穿墙".例如我们可以把经过 ...
- bzoj 2281: [Sdoi2011]黑白棋
再次,,,,,虚(一开始看错题了,看成一次移动一个棋子,能移动1-d个格子...这样的话有没有大神会做??本蒟蒻就教) 额,,直接%%%%把...http://hzwer.com/5760.html ...
- 使用git提交远程仓库
git pull 更新 git add 文件名 将文件添加到暂存区 git commit -m ‘注释’ 提交 git push origin master 提交到远程仓库
- eclipse中使用jstl
错误提示为"can not find the tag library for http://java.sun.com/jsp/jstl/core" 这是我在练习把axis2和普通j ...
- 【STM32H7教程】第52章 STM32H7的LTDC应用之点阵字体和字符编码(重要)
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第52章 STM32H7的LTDC应用之点阵字体和 ...
- JavaWeb开发校园二手平台项目 源码
开发环境: Windows操作系统开发工具:MyEclipse/Eclipse + JDK+ Tomcat + MySQL 数据库 项目简介: JAVAWEB校园二手平台项目,基本功能包括:个人信息. ...
- C# 并行线程调用
参考 一.异步委托开启线程 Action<int, int> a = add; a.BeginInvoke(, , null, null);//前两个是add方法的参数,后两个可以为空 C ...
- 留学萌新Essay写作须知
Essay是留学生们接触比较多的一项留学生作业,但尽管如此,依旧有部分同学对于essay写作是没有足够的把握的.随着开学季的到来,很多萌新初次接触Essay写作,难免会有很多不懂得地方.所以今天小编就 ...
- js基础学习之-js全局对象
声明的三种方式: 第一种: var test; //或var test = 5; 第二种: test = 5; 第三种: window.test; //或window.test = 5; //只是使用 ...