一、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. C# DataSet导出Excel

    //多个DataSet导出Excel文件 public static void DataSetToExcel(DataSet p_ds,string strSavePath) { ;//多个DataS ...

  2. [转]Python中下划线以及命名空间的意义

    Python 用下划线作为变量前缀和后缀指定特殊变量/方法. 主要存在四种情形 1. 1. object # public    2. __object__ # special, python sys ...

  3. Gradle Goodness: Check Task Dependencies With a Dry Run

    We can run a Gradle build without any of the task actions being executed. This is a so-called dry ru ...

  4. 搭建Hadoop2.6.0+Spark1.1.0集群环境

    前几篇文章主要介绍了单机模式的hadoop和spark的安装和配置,方便开发和调试.本文主要介绍,真正集群环境下hadoop和spark的安装和使用. 1. 环境准备 集群有三台机器: master: ...

  5. 07.安装及使用gitlub

    博客为日常工作学习积累总结: 1.安装gitlub sudo yum install -y curl policycoreutils-python openssh-server openssh-cli ...

  6. IoC和AOP扩展

    一.构造注入 二.使用p命名空间注入属性值 三.注入不同数据类型 <?xml version="1.0" encoding="UTF-8"?> &l ...

  7. 【sql server常用操作{增删改查}】

    use DB_x   go   drop database DB_y   create database DB_y --创建数据库   on primary --指定主数据文件   (   name= ...

  8. Hadoop入门学习路线

    走上大数据的自学之路....,Hadoop是走上大数据开发学习之路的第一个门槛. Hadoop,是Apache的一个开源项目,开发人员可以在不了解分布式底层细节,开发分布式程序,充分利用集群进行高速运 ...

  9. Drill-On-YARN

    1. Drill-On-YARN介绍 功能 启动 停止 扩容 缩容 failover 启动流程 下载drill的社区包,进行必要的配置,执行drill-on-yarn.sh start命令,启动dri ...

  10. C# 调用腾讯云接口获取视频基本信息

    做项目需要上传视频,获取时长,上传教程很多,获取信息很少,官方只有一条请求地址. 找了好久,都没有说这个请求地址怎么用.最后发现需要调用腾讯云SDK 官方地址:https://github.com/Q ...