Java并发基础--Lock的学习
一、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的学习的更多相关文章
- java并发基础及原理
		java并发基础知识导图 一 java线程用法 1.1 线程使用方式 1.1.1 继承Thread类 继承Thread类的方式,无返回值,且由于java不支持多继承,继承Thread类后,无法再继 ... 
- Java 并发基础
		Java 并发基础 标签 : Java基础 线程简述 线程是进程的执行部分,用来完成一定的任务; 线程拥有自己的堆栈,程序计数器和自己的局部变量,但不拥有系统资源, 他与其他线程共享父进程的共享资源及 ... 
- Java并发基础概念
		Java并发基础概念 线程和进程 线程和进程都能实现并发,在java编程领域,线程是实现并发的主要方式 每个进程都有独立的运行环境,内存空间.进程的通信需要通过,pipline或者socket 线程共 ... 
- 【搞定 Java 并发面试】面试最常问的 Java 并发基础常见面试题总结!
		本文为 SnailClimb 的原创,目前已经收录自我开源的 JavaGuide 中(61.5 k Star![Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.欢迎 Sta ... 
- 【Java并发基础】并发编程领域的三个问题:分工、同步和互斥
		前言 可以将Java并发编程抽象为三个核心问题:分工.同步和互斥. 这三个问题的产生源自对性能的需求.最初时,为提高计算机的效率,当IO在等待时不让CPU空闲,于是就出现了分时操作系统也就出现了并发. ... 
- java并发基础(五)--- 线程池的使用
		第8章介绍的是线程池的使用,直接进入正题. 一.线程饥饿死锁和饱和策略 1.线程饥饿死锁 在线程池中,如果任务依赖其他任务,那么可能产生死锁.举个极端的例子,在单线程的Executor中,如果一个任务 ... 
- java并发基础(二)
		<java并发编程实战>终于读完4-7章了,感触很深,但是有些东西还没有吃透,先把已经理解的整理一下.java并发基础(一)是对前3章的总结.这里总结一下第4.5章的东西. 一.java监 ... 
- Java并发包下锁学习第二篇Java并发基础框架-队列同步器介绍
		Java并发包下锁学习第二篇队列同步器 还记得在第一篇文章中,讲到的locks包下的类结果图吗?如下图:  从图中,我们可以看到AbstractQueuedSynchronizer这个类很重要(在本 ... 
- 【Java并发基础】利用面向对象的思想写好并发程序
		前言 下面简单总结学习Java并发的笔记,关于如何利用面向对象思想写好并发程序的建议.面向对象的思想和并发编程属于两个领域,但是在Java中这两个领域却可以融合到一起.在Java语言中,面向对象编程的 ... 
随机推荐
- C# DataSet导出Excel
			//多个DataSet导出Excel文件 public static void DataSetToExcel(DataSet p_ds,string strSavePath) { ;//多个DataS ... 
- [转]Python中下划线以及命名空间的意义
			Python 用下划线作为变量前缀和后缀指定特殊变量/方法. 主要存在四种情形 1. 1. object # public 2. __object__ # special, python sys ... 
- 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 ... 
- 搭建Hadoop2.6.0+Spark1.1.0集群环境
			前几篇文章主要介绍了单机模式的hadoop和spark的安装和配置,方便开发和调试.本文主要介绍,真正集群环境下hadoop和spark的安装和使用. 1. 环境准备 集群有三台机器: master: ... 
- 07.安装及使用gitlub
			博客为日常工作学习积累总结: 1.安装gitlub sudo yum install -y curl policycoreutils-python openssh-server openssh-cli ... 
- IoC和AOP扩展
			一.构造注入 二.使用p命名空间注入属性值 三.注入不同数据类型 <?xml version="1.0" encoding="UTF-8"?> &l ... 
- 【sql server常用操作{增删改查}】
			use DB_x go drop database DB_y create database DB_y --创建数据库 on primary --指定主数据文件 ( name= ... 
- Hadoop入门学习路线
			走上大数据的自学之路....,Hadoop是走上大数据开发学习之路的第一个门槛. Hadoop,是Apache的一个开源项目,开发人员可以在不了解分布式底层细节,开发分布式程序,充分利用集群进行高速运 ... 
- Drill-On-YARN
			1. Drill-On-YARN介绍 功能 启动 停止 扩容 缩容 failover 启动流程 下载drill的社区包,进行必要的配置,执行drill-on-yarn.sh start命令,启动dri ... 
- C# 调用腾讯云接口获取视频基本信息
			做项目需要上传视频,获取时长,上传教程很多,获取信息很少,官方只有一条请求地址. 找了好久,都没有说这个请求地址怎么用.最后发现需要调用腾讯云SDK 官方地址:https://github.com/Q ... 
