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的更多相关文章

  1. Java 并发:Lock 框架详解

    摘要: 我们已经知道,synchronized 是java的关键字,是Java的内置特性,在JVM层面实现了对临界资源的同步互斥访问,但 synchronized 粒度有些大,在处理实际问题时存在诸多 ...

  2. java并发库 Lock 公平锁和非公平锁

    jdk1.5并发包中ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,关于两者区别,java并发编程实践里面有解释 公平锁:   Threads acquir ...

  3. Java并发编程:Lock

    Java并发编程:Lock 在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.l ...

  4. Java并发分析—ConcurrentHashMap

    LZ在 https://www.cnblogs.com/xyzyj/p/6696545.html 中简单介绍了List和Map中的常用集合,唯独没有CurrentHashMap.原因是CurrentH ...

  5. Java并发分析—volatile

    在https://www.cnblogs.com/xyzyj/p/11148497.html中已经说明了在多线程并发的情况下,会出现数据的不一致问题,但归根结底就是一个原因,在宏观上就是线程的执行顺序 ...

  6. Java并发分析—synchronized

    在计算机操作系统中,并发在宏观上是指在同一时间段内,同时有多道程序在运行. 一个程序可以对应一个进程或多个进程,进程有独立的存储空间.一个进程包含一个或多个线程.线程堆空间是共享的,栈空间是私有的.同 ...

  7. Java并发基础--Lock的学习

    一.Lock的出现 Lock的主要作用实现线程之间的同步互斥,与synchronized关键字的效果是一样的,synchronized是Java语言内置的特性,那么为什么又出现了Lock呢?原因是sy ...

  8. java 并发编程lock使用详解

    浅谈Synchronized: synchronized是Java的一个关键字,也就是Java语言内置的特性,如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,执行代码块时,其 ...

  9. Java并发编程(07):Fork/Join框架机制详解

    本文源码:GitHub·点这里 || GitEE·点这里 一.Fork/Join框架 Java提供Fork/Join框架用于并行执行任务,核心的思想就是将一个大任务切分成多个小任务,然后汇总每个小任务 ...

随机推荐

  1. 安卓多个按钮使用一个OnClickListener

    安卓studio 3.1 版本编译通过 一个按钮id为bt1 一个按钮Id为bt2 mainactivity 代码入下 package com.example.vmpdump.firstapp; im ...

  2. Mysql多对多关系的查询

    1.创建user表 2.创建gateway表 3.创建user_gateway表 4.创建device表 5.创建gateway_device表 6.创建一个实体类 public class Devi ...

  3. Java 日期与时间

    章节 Java 基础 Java 简介 Java 环境搭建 Java 基本语法 Java 注释 Java 变量 Java 数据类型 Java 字符串 Java 类型转换 Java 运算符 Java 字符 ...

  4. redis.conf文件配置

    最重要三个配置 1. bind 127.0.0.1 需要注释掉这一行,使别的主机可以访问 2. daemonize no 需要改为yes,使其后台运行 3. requirepass foobared ...

  5. 解决Elasticsearch索引只读

    今天添加索引时发现kibana添加索引不生效,页面也没有报错,没有创建成功只是一闪而过. 另外发现各项目日志与当前时间差异很大,filebeat一直报错io timeout 具体报错如下: fileb ...

  6. JavaWeb开发校园二手平台项目 源码

    开发环境: Windows操作系统开发工具:MyEclipse/Eclipse + JDK+ Tomcat + MySQL 数据库 项目简介: JAVAWEB校园二手平台项目,基本功能包括:个人信息. ...

  7. 七十五、SAP中数据库的使用SQL

    一.在SAP中可以使用两张数据库,一直是NativeSQL和OPEN SQL. Native SQL(本地SQL)特点: 1.每种关系型数据库都有其对应的  SQL,是数据库相关的. 2.不同的 SA ...

  8. 136-PHP 使用类名访问static修饰的类方法

    <?php class test{ //定义一个类 public static function class_info(){ //定义类方法 return '这是一个用于测试的类.'; } } ...

  9. AFNetworking实现表单(multipart)形式上传图片

    最近遇到个问题,就是上传图片到服务器,后台说用表单形式... 由于没弄过这种上传,所以搜了大堆资料,但也没解决问题. 最后通过请教一位大神才得以解决这个简单的问题... 现在将此方法做个笔记... & ...

  10. opencv运动物体识别

    import cv2 import time import datetime import os def mkdir(path): folder = os.path.exists(path) if n ...