一、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. 移动端meta标签的设置

    var phoneWidth = parseInt(window.screen.width); var phoneScale = phoneWidth / 640; var ua = navigato ...

  2. POJ 1949 Chores (很难想到的dp)

    传送门: http://poj.org/problem?id=1949 Chores Time Limit: 3000MS   Memory Limit: 30000K Total Submissio ...

  3. 利用python 传输文件

    最近在学python3 发现了一个很有用的功能,该功能可以将安装python 的机器作为一台http 服务器来分享本机的文件, 具体的使用记录如下 python3 的使用方法 直接在windows 的 ...

  4. 点击HTML页面问号出现提示框

    本demo的功能:点击页面按钮在其边缘出现提示信息,点击页面任何一处则消失. 如下图: 1.所需插件: jquery插件: layer插件: 2.HTML内容: ==注意==: class=" ...

  5. unittest单元测试框架之测试环境的初始化与还原(fixture)(五)

    1.方法一:针对每条测试用例进行初始化与还原 import unittest from UnittestDemo.mathfunc import * class TestMathFunc(unitte ...

  6. 基于java的简易计算器实现

    方法: 1.将string类型的表达式输入转换成后缀表达式 2.计算后缀表达式 步骤一:将string类型的表达式输入转换成后缀表达式 输入字符串表达式,并将表达式转换成char型数组 String ...

  7. SQLMAP使用详解

    使用示例 python sqlmap.py -u "http://xx.com/member.php?id=XX"  -p id --dbms "Mysql"  ...

  8. HSL与RGB颜色转换

    /** * HSL颜色值转换为RGB. * 换算公式改编自 http://en.wikipedia.org/wiki/HSL_color_space. * h, s, 和 l 设定在 [0, 1] 之 ...

  9. LogViewer超大文本浏览工具

    官方下载 LogViewer 是一款简单好用的log日志文件查看工具.您想要查看log日志吗?那么不妨来看看这款LogViewer .该款工具可以在短短数秒内打开上G的LOG文件,支持高亮某行文字(例 ...

  10. 如何给ioloop.run_sync()中调用的函数传入参数

    问题 如何给tornado.ioloop.IOLoop中的run_sync方法中调用的函数添加参数 解决方案 使用functools.partial 解决示例 from tornado import ...