一、Lock的出现

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

①、通过synchronized实现同步,如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。

②、通过synchronized实现同步的时候,无法知道线程是否获取到了锁。

为了弥补synchronized的缺陷,Java 5中引入了新的锁机制——java.util.concurrent.locks中的显式的互斥锁。Lock接口,提供了比synchronized更加广泛的锁操作。它的主要实现类:ReentrantLock、ReetrantReadWriteLock.ReadLock和ReetrantRead.WriteLock.即重入锁、读锁和写锁。Lock必须被显式的创建和释放,为了保证最终锁一定被释放,经常将虎互斥区置放在try语句中,并在finally中释放锁,特别是当有return语句的时候,return语句必须放在try语句中,以确保unlock不会过早的发生,从而将数据暴露给下面的任务。使用比较多的是ReentrantLock。示例如下:

 public class LockDemo {
private Lock lock = new ReentrantLock(); public void testMethod(){
try {
lock.lock();
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName()+" print....");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
lock.unlock();
}
} public static void main(String[] args) {
LockDemo lockDemo = new LockDemo(); new Thread(new MyThread1(lockDemo)).start();
new Thread(new MyThread1(lockDemo)).start(); } }
class MyThread1 implements Runnable{
private LockDemo lockDemo;
public MyThread1(LockDemo lockDemo){
this.lockDemo = lockDemo;
}
@Override
public void run() {
lockDemo.testMethod();
} }

结果输出:

Thread-0 print....
Thread-0 print....
Thread-0 print....
Thread-1 print....
Thread-1 print....
Thread-1 print....

总结:

  • Lock是一个接口,它有不同的实现类,通过它的实现类可以实现多线程间的同步互斥。
  • Lock与synchronized最大的不同在于,synchronized不需要用户手动释放锁,在线程完成了同步块或者同步方法后自动释放锁(或者出现异常也自动释放锁)。而Lock锁需要用户手动释放,如果忘记释锁,可能造成死锁等后果。

二、Lock的详细学习

通过源码可以知道,Lock是一个接口,lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()是用来获取锁的。unLock()方法是用来释放锁的。newCondition()主要是用来创建Condition对象的,线程对象可以注册在指定的Condition上,Condition起到一个对象监视器的作用,通过Condition实例从而进行线程通知,在调用线程上更加灵活。

1.lock()方法

lock方法是使用最多的一个方法,即用来获取锁的,如果锁已经被其他线程占用,则进行等待。经常需要结合try语句一起使用。目的是为了将锁的释放工作放在finally中进行,保证锁的最终释放。如下:

Lock lock = new ReentrantLock();

lock.lock();
try{
//处理任务
}catch(Exception ex){ }finally{
lock.unlock(); //释放锁
}

2.tryLock()方法

tryLock()是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。

3.tryLock(long time, TimeUnit unit)方法

tryLock(long time, TimeUnit unit)方法类似tryLock(),区别在于这个方法在拿不锁的时候,不会立即返回,而是等待指定的时间,如果超过指定时间还未获取到锁,则立即返回false,获取锁失败。

Lock lock = new ReentrantLock();

if(lock.tryLock(3, TimeUnit.SECONDS)) {
try{
//处理任务
}catch(Exception ex){ }finally{
lock.unlock(); //释放锁
}
}else {
//如果不能获取锁,则直接做其他事情
}

4.lockInterruptibly()方法

lockInterruptibly()方法,比较特使,可以说是Lock锁的特点之一,即线程在获取锁的过程中,如果一直获取不到锁,它不会立即放弃,直到这个线程被中断。即它能够响应中断, void lockInterruptibly() throws InterruptedException;接口的方法定义是声明了异常的,lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出InterruptedException。它的使用形式如下:

public void method() throws InterruptedException {

    lock.lockInterruptibly();
try {
//.....
}
finally {
lock.unlock();
}
}

5.newCondition()方法

在使用notify()/notifyAll()方法的时候,被唤醒的线程是JVM随机选择的,最终能够获取锁的线程也许不是我们想要的。在Lock中结合Condition类可以实现“选择性通知”。即可以通过Condition对象唤醒指定的线程。示例如下:

 public class LockConditionDemo {

     private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition(); public void testMethod(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+" test begin");
condition.await();//当前线程进入等待状态,释放lock锁
System.out.println(Thread.currentThread().getName()+" test end");
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();
System.out.println(Thread.currentThread().getName()+" 释放锁");
}
} public void testMethod2(){
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+" test2 begin");
condition.signal();//通知处于等待lock锁状态的线程
System.out.println(Thread.currentThread().getName()+" test2 end");
}finally{
lock.unlock();
System.out.println(Thread.currentThread().getName()+" 释放锁");
}
} public static void main(String[] args) {
LockConditionDemo lockConditionDemo = new LockConditionDemo();
new Thread(new Mythread3(lockConditionDemo)).start();
new Thread(new Mythread4(lockConditionDemo)).start();
} } class Mythread3 implements Runnable{
private LockConditionDemo lockConditionDemo; Mythread3(LockConditionDemo lockConditionDemo){
this.lockConditionDemo = lockConditionDemo;
} @Override
public void run() {
lockConditionDemo.testMethod();
}
} class Mythread4 implements Runnable{
private LockConditionDemo lockConditionDemo; Mythread4(LockConditionDemo lockConditionDemo){
this.lockConditionDemo = lockConditionDemo;
} @Override
public void run() {
lockConditionDemo.testMethod2();
}
}

结果输出:

Thread-0 test begin
Thread-1 test2 begin
Thread-1 test2 end
Thread-1 释放锁
Thread-0 test end
Thread-0 释放锁

三、Lock和synchronized的选择

1.lock和synchronized的不同

  • Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现。

  • synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁。

  • Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断

  • 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到

  • Lock可以提高多个线程进行读操作的效率

Java并发基础--Lock的学习的更多相关文章

  1. java并发基础及原理

    java并发基础知识导图   一 java线程用法 1.1 线程使用方式 1.1.1 继承Thread类 继承Thread类的方式,无返回值,且由于java不支持多继承,继承Thread类后,无法再继 ...

  2. Java 并发基础

    Java 并发基础 标签 : Java基础 线程简述 线程是进程的执行部分,用来完成一定的任务; 线程拥有自己的堆栈,程序计数器和自己的局部变量,但不拥有系统资源, 他与其他线程共享父进程的共享资源及 ...

  3. Java并发基础概念

    Java并发基础概念 线程和进程 线程和进程都能实现并发,在java编程领域,线程是实现并发的主要方式 每个进程都有独立的运行环境,内存空间.进程的通信需要通过,pipline或者socket 线程共 ...

  4. 【搞定 Java 并发面试】面试最常问的 Java 并发基础常见面试题总结!

    本文为 SnailClimb 的原创,目前已经收录自我开源的 JavaGuide 中(61.5 k Star![Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.欢迎 Sta ...

  5. 【Java并发基础】并发编程领域的三个问题:分工、同步和互斥

    前言 可以将Java并发编程抽象为三个核心问题:分工.同步和互斥. 这三个问题的产生源自对性能的需求.最初时,为提高计算机的效率,当IO在等待时不让CPU空闲,于是就出现了分时操作系统也就出现了并发. ...

  6. java并发基础(五)--- 线程池的使用

    第8章介绍的是线程池的使用,直接进入正题. 一.线程饥饿死锁和饱和策略 1.线程饥饿死锁 在线程池中,如果任务依赖其他任务,那么可能产生死锁.举个极端的例子,在单线程的Executor中,如果一个任务 ...

  7. java并发基础(二)

    <java并发编程实战>终于读完4-7章了,感触很深,但是有些东西还没有吃透,先把已经理解的整理一下.java并发基础(一)是对前3章的总结.这里总结一下第4.5章的东西. 一.java监 ...

  8. Java并发包下锁学习第二篇Java并发基础框架-队列同步器介绍

    Java并发包下锁学习第二篇队列同步器 还记得在第一篇文章中,讲到的locks包下的类结果图吗?如下图: ​ 从图中,我们可以看到AbstractQueuedSynchronizer这个类很重要(在本 ...

  9. 【Java并发基础】利用面向对象的思想写好并发程序

    前言 下面简单总结学习Java并发的笔记,关于如何利用面向对象思想写好并发程序的建议.面向对象的思想和并发编程属于两个领域,但是在Java中这两个领域却可以融合到一起.在Java语言中,面向对象编程的 ...

随机推荐

  1. libstagefright 音视频同步方案

    1:音视频数据都有一个list,用于存放解码后的数据:    List mFilledBuffers; 2:解码后的音视频数据不断的往list中存放,不做音视频同步方面的时间上控制    mFille ...

  2. Java 编码规范(转)

    本文转自:http://www.javaranger.com/archives/390 文章总结出了java编码过程中的一些规范,以便参考. 1.合理组织代码层次,分层清晰:controller.lo ...

  3. JSP Cookie 处理

    Cookie是存储在客户机的文本文件,它们保存了大量轨迹信息.在servlet技术基础上,JSP显然能够提供对HTTP cookie的支持. 通常有三个步骤来识别回头客: 服务器脚本发送一系列cook ...

  4. App 运行后屏幕顶部和底部各留黑边问题 - iOS

    App 启动后屏幕的顶部和底部各产生一条黑边,App 的内容会被压缩在两个黑条内显示,比例失调的情况. 初步判断是启动页资源图片适配问题. 首先,查看工程 project => General ...

  5. ZooKeeper分布式

    1:zk的相关特性 1.一致性:数据一致性,数据按顺序分批入库. 2.原子性:事务要么都成功,要么都失败,不会局部化. 3.单一视图:客户端连接集群中的任一zk节点,数据都是一致的. 4.可靠性:每次 ...

  6. mybatis传单个参数,和<if>标签同时使用的问题

    // Mapper.java EmerEvent selectByAlarmId(Integer alarmId); // Mapper.xml <select id="selectB ...

  7. windows用交互式命令执行python程序

    1.进入cmd命令 windows+r2.进入盘符,eg:E:3.使用dir命令查看当前文件夹下的所有目录4.使用绝对路径或者相对路径和cd命令直接进入想要到达的文件夹目录(或者使用cd命令一步一步达 ...

  8. C++笔记009:C++对C的扩展——“实用性”增加

    原创笔记,转载请注明出处! 点击[关注],关注也是一种美德~ 笔记八中已经说到:在 C语言中定义了3个层次的作用域,即文件(编译单元).函数和复合语句. C语言中的变量都必须在作用域开始的位置定义! ...

  9. 转:Java并发集合

    引自:http://ifeve.com/concurrent-collections-1/ 并发集合(一)引言 声明:本文是< Java 7 Concurrency Cookbook>的第 ...

  10. H3C Telnet 配置-01

    Telnet 配置管理方法是网络工程师和网络管理员使用最广泛的一种设备访问控制方法,它通过局域网或广域网实现本地或远程的访问控制,但是它的实验必须要求首先对设备进行初始化配置,否则用户无法正常登录和访 ...