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基础——标识符、关键字和基础数据类型
Java标识符: 1.由英文字母.数字._(下划线)和$组成,长度不限.其中英文字母包含大写字母(A~Z)和小写字母(a~z),数字包含0到9 2.标识符的第一个字符不能是数字(即标识符不能以数字开头 ...
- kali linux终端快捷键设置
kali里面是没有终端快捷键的,需要自己设置. 打开设置->设备->keyboard,拉到最下面点击加号来新建一个快捷键. 名称:打开终端 命令:gnome-terminal 快捷键:Ct ...
- B. Yet Another Crosses Problem
B. Yet Another Crosses Problem time limit per test 2 seconds memory limit per test 256 megabytes inp ...
- Codeforces Round #619 (Div. 2)
A. Three Strings 题意:给三个长度相同的非空字符串abc,依次将c中的每个字符和a或者b中对应位置的字符进行交换,交换必须进行,问能否使得ab相同. 思路:对于每一个位置,如果三个字符 ...
- 转 centos7使用kubeadm安装kubernetes 1.12版本
最近在学习go 模板语法 还是有点小忙 ! 感觉写这种 kuberadm 搭建 kubernetes 还是有点水 好吧 可能我太高调了 前前后后搭过四 五次了 ! kuberadm 搭建 ...
- HihoCoder第十二周:刷油漆
#1055 : 刷油漆 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上回说到,小Ho有着一棵灰常好玩的树玩具!这棵树玩具是由N个小球和N-1根木棍拼凑而成,这N个小球 ...
- WebStorm 使用经验
1.优点 1.1 可自动提示图片的宽高 1.2 标签名字可重构(改名) 1.3 css重命名 1.4 可把内联的style移到外部 1.5 可实现声明提升 1.6 设置项是可搜索的 1.7 有 ...
- eclipse 安装spring tools suite插件
之前使用idea进行springboot项目的开发学习,但是由于idea是收费的,总是用着用着说验证码到期之类的,总之还是很不爽,于是就想重新采用eclipse开发springboot项目,为了方便s ...
- linux下安装jdk&&Tomcat环境
linux系统 Centos6 下部署应用服务 jdk-1.7 环境安装:(切换到root用户下操作)1. 在 /usr/local 目录下创建jdk7文件 mkdir /usr/local/jdk7 ...
- BZOJ 3332
题解:给边赋上权值,然后求最大生成树,如果不符合那就无解 证明:留坑 #include<iostream> #include<cstdio> #include<cstri ...