Lock简介
digest
synchronized已经提供了锁的功能,而且还是Java的内置特性,那为什么还要出现lock呢?
用一句话来讲就是——synchronized可以实现同步,但太死板了它的同步机制;lock可以提供更灵活更丰富的同步、并发机制,提供了许多有用的功能。
synchronized的缺陷:
1. 锁的释放不灵活
如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
2)线程执行发生异常,此时JVM会让线程自动释放锁。
那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。
因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到。
2. 多个线程无法一起读
当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象。
但是采用synchronized关键字来实现同步的话,就会导致一个问题:
如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。
因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。
3. 使用者对锁的掌控能力低
使用synchronized的时候,获得锁、释放锁都是jvm帮你搞定的,当你的线程走过synchronized的方法或代码块的时候,就会尝试去获得锁对象的monitor从而获得锁,无法获得就堵塞在那,一直等待;释放锁也是,获得锁的线程走完临界区的代码,就自己会释放锁,不用程序员操作什么。
所以,用户无法知道线程是否获得锁了,Lock可以做到这个。
还有就是,Lock需要你手动去释放锁,如果没有手动释放,可能导致出现死锁现象。
简单介绍cus包下常用的接口和类
Lock接口
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
里面的方法都顾名思义,就提一下lockInterruptibly()方法。
当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
由于lockInterruptibly()的声明中抛出了异常,所以lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出InterruptedException。
tips:
由于在前面讲到如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生
一般像这样用:
Lock lock = ...;
lock.lock();
try{
//处理任务
}catch(Exception ex){
}finally{
lock.unlock(); //释放锁
}
ReentrantLock
意思是重入锁,唯一实现Lock接口的类。
ReadWriteLock接口
这也是个重要的接口,里面之定义了两个方法
/**
* Returns the lock used for reading.
*
* @return the lock used for reading.
*/
Lock readLock();
/**
* Returns the lock used for writing.
*
* @return the lock used for writing.
*/
Lock writeLock();
一个用来获取读锁,一个用来获取写锁。也就是说将文件的读写操作分开,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。
ReentrantReadWriteLock
这是个实现了ReadWriteLock接口的类。
不过要注意的是:
如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。
如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。
关于ReentrantReadWriteLock类中的其他方法感兴趣的朋友可以自行查阅API文档。
Lock和synchronized的选择
总结来说,Lock和synchronized有以下几点不同:
1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
5)Lock可以提高多个线程进行读操作的效率。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。
参考文章:
http://www.cnblogs.com/dolphin0520/p/3923167.html——《Java并发编程:Lock 》有用法,有概念介绍,基本就是照着这个写的hh。
Lock简介的更多相关文章
- 并发和多线程(三)--并发容器J.U.C和lock简介
AQS: 是AbstractQueuedSynchronizer的简称,JUC的核心 底层是sync queue双向链表,还可能有condition queue单向链表,使用Node实现FIFO队列, ...
- java多线程(3)---synchronized、Lock
synchronized.Lock 一.概述 1.出现线程不安全的原因是什么? 如果我们创建的多个线程,存在着共享数据,那么就有可能出现线程的安全问题:当其中一个线程操作共享数据时,还未操作完成,另外 ...
- 8.初识Lock与AbstractQueuedSynchronizer(AQS)
1. concurrent包的结构层次 在针对并发编程中,Doug Lea大师为我们提供了大量实用,高性能的工具类,针对这些代码进行研究会让我们对并发编程的掌握更加透彻也会大大提升我们队并发编程技术的 ...
- Ticket Lock, CLH Lock, MCS Lock
如果不用OS提供的mutex,我们该如何实现互斥锁?(不考虑重入的情况) 1. naive lock 最简单的想法是,搞一个volatile类型的共享变量flag,值可以是flase(无锁)或者tru ...
- 初识Lock与AbstractQueuedSynchronizer(AQS)
本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...
- ReentrantLock实现原理
以下是本篇文章的大纲 1 synchronized和lock 1.1 synchronized的局限性 1.2 Lock简介 2 AQS 3 lock()与unlock()实现原理 3.1 基础知识 ...
- AQS底层原理分析
J.U.C 简介 Java.util.concurrent 是在并发编程中比较常用的工具类,里面包含很多用来在并发场景中使用的组件.比如线程池.阻塞队列.计时器.同步器.并发集合等等.并发包的作者是大 ...
- java 多线程-3
十.同步机制解决Thread继承安全问题 创建三个窗口买票,共100张票.用继承来实现 方式一:同步代码块 public class RunMainExtends { public static vo ...
- 多线程(四) AQS底层原理分析
J.U.C 简介 Java.util.concurrent 是在并发编程中比较常用的工具类,里面包含很多用来在并发 场景中使用的组件.比如线程池.阻塞队列.计时器.同步器.并发集合等等.并 发包的作者 ...
随机推荐
- WebDriver API——延时操作及元素等待
在自动化测试过程当中,受网络.测试设备等诸多因素的影响,我们经常需要在自动化测试脚本中添加一些延时来更好的定位元素来进行一系列的操作. 一般有这么几种方式: 1.implicitlyWait.识别对象 ...
- 如何刷新本地的DNS缓存?
为了提高网站的访问速度,系统会在成功访问某网站后将该网站的域名.IP地址信息缓存到本地.下次访问该域名时直接通过IP进行访问.一些网站的域名没有变化,但IP地址发生变化,有可能因本地的DNS缓存没有刷 ...
- 使用IIS建立主机到虚拟机的端口转发
主机是笔记本电脑,通过一个TPLINK智能扩展卡,作为服务器供给手机APP当作服务器. 但真正的Web服务,在主机的VMWare Workstation虚拟机80端口. 那么主机和手机形成的网络为19 ...
- rc.local 开启自启动,检测是否成功
rc.local /etc/init.d/nginx start 查看运行状态 systemctl status rc-local ● rc-local.service - /etc/rc.local ...
- 中缀表达式std
#include<cstdio>#include<cstdlib>#include<string>#include<cstring>using name ...
- python BaseManager分布式学习
如果我们已经有一个通过Queue通信的多进程程序在同一台机器上运行,现在,由于处理任务的进程任务繁重,希望把发送任务的进程和处理任务的进程分布到两台机器上.怎么用分布式进程实现?原有的Queue可以继 ...
- bzoj 2836 魔法树——树链剖分
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2836 树剖裸题.然而WA.RE了好久…… 原来是跳 top 的那个地方! top 不相等的时 ...
- Spring的自学之路之入门
认识Spring Spring是分层的Java SE/EE 应用一站式的轻量级开源框架,以Ioc(Inverse of Control,控制反转)和AOP(Aspect Oriented Progra ...
- CF-831C
C. Jury Marks time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...
- [开源]OSharpNS - .net core 快速开发框架 - 快速开始
什么是OSharp OSharpNS全称OSharp Framework with .NetStandard2.0,是一个基于.NetStandard2.0开发的一个.NetCore快速开发框架.这个 ...