密码锁 java接口_从synchronized和lock区别入手聊聊java锁机制

写这篇文章之前,我去百度了一下啥叫锁,百度百科上写道:置于可启闭的器物上,以钥匙或暗码开启。确实我们一般理解的锁就是门锁,密码锁,但是在计算机科学中,锁又是啥,说实话,这个问题我也思考了很久,也没法很好的用一两句话就让人听得明白,也不想有人看到我的文章,然后将我的结论当作答案,我觉得最好的答案还是在探索的过程中得到的,接下来我们就好好探索一番。

作为一名java程序员,最开始接触到的锁就是synchronized,书本上是这么写的,老师也是这么说的,至于为啥叫锁,可能也没多少人真的去思考过。不知道有没有同学和我一样,经历过只知道用synchronized,后来逐渐的了解ReentrantLock,读写锁,然后又了解了aqs,后来通过百度google,看一些博客(这个我要吐槽一下,在学习过程中遇到过很多文章写的有问题的,反而误导了我),后面看了看synchronized的源码,最后对比synchronized和ReentrantLock才加深了对锁的一些认知(说实话,作为一个刚毕业3年的非科班出身码农,我也不敢保证自己写的就一定对,算是学习过程中的一些感悟吧),那接下来我就按照学习顺序来逐渐展开。

先来一段简单的synchronized使用代码:

public static voidmain(String[] args) {

String s= newString();synchronized(s) {

TestJni jni= newTestJni();

jni.jniHello();

}

}

上面代码做的事情很简单,如下图所示,有A B C D E多个线程同时来到synchronized包含的代码块,A先一步进来了,那么BCDE都得等,等我A执行完他们才能进来执行。

97294f2369017762a0923dfab5481ebf.png       

c50908cf6a170793e787700e41779e04.png

synchronized用起来确实很简单,我们也可以放在方法上,但是其本质还是锁的对象,这个我们后面分析源码一看就知道了。

随着开发时间越长,synchronized在有些复杂场景下(如需要可中断,可控制时间抢锁,需要多个等待队列分别控制,读写锁等场景的时候)无法满足我们的需求,那么就要用到Lock,下面我们先介绍一下Lock的简单使用:

Lock lock = newReentrantLock();

lock.lock();try{

System.out.println("线程:"+Thread.currentThread().getName()+ " 进来啦");

}finally{

lock.unlock();

}

上面是一种最简单的使用,和synchronized作用是一样的,不过加锁之后必须要解锁,且必须紧跟try - finally块解锁,使用起来稍微复杂一点,容易出错。

我们再介绍一种可中断的使用方式:

public static voidmain(String[] args) {

Thread thread= new Thread(() ->{try{

lock.lockInterruptibly();try{

testLock();

}finally{

lock.unlock();

}

}catch(Exception e){

}

});

thread.start();

thread.interrupt();

}public static voidtestLock(){

condition.signalAll();

System.out.println("线程:"+Thread.currentThread().getName()+ " 进来啦");

}

这种方式呢,在拿锁被park住了,如果刚好这时候被打断了,就会响应打断退出抢锁并抛出异常,至于捕获到异常开发者怎么做,那就得根据业务来分别处理了。

而像可控制时间的其实就要稍微复杂一点,先看一下synchronized中的使用:

static TestHash s = newTestHash();public static voidmain(String[] args) {

Thread thread1= new Thread(()->{

testLock();

});

Thread thread2= new Thread(()->{synchronized(s) {try{

TimeUnit.SECONDS.sleep(1);

}catch(InterruptedException e) {

}

s.notify();

testLock();

}

});

thread1.start();

thread2.start();

}public static voidtestLock(){synchronized(s) {

System.out.println("线程:"+Thread.currentThread().getName()+ " 进来啦");try{

s.wait();

System.out.println("线程:"+Thread.currentThread().getName()+ " 叫醒啦");

}catch(InterruptedException e) {

System.out.println("抛异常啦");

}

}

}

这个例子看着要比前面几个复杂一点,首先thread1会进入testLock方法,并拿到锁,thread2等了1秒叫醒thread1(这里就是简单的wait/notify的使用),然后在拿到锁的情况下,再次进入testLock方法并拿到锁,由于没人唤醒了,会一直卡在这里(这里证明了synchronized的可重入),结果我就不贴了,感兴趣的可以拿着代码去试。

而ReentrantLock的使用也差不多,就是提前用lock去new一个Condition:

static ReentrantLock lock = newReentrantLock();static Condition condition =lock.newCondition();public static voidmain(String[] args) {

Thread thread1= new Thread(()->{

testWaitSingal();

},"thread1");

Thread thread2= new Thread(()->{

lock.lock();try{

TimeUnit.SECONDS.sleep(1);

condition.signal();

testWaitSingal();

}catch(InterruptedException e) {

}finally{

lock.unlock();

}

},"thread2");

thread1.start();

thread2.start();

}public static voidtestWaitSingal(){

lock.lock();try{

System.out.println("线程:"+Thread.currentThread().getName()+ " 进来啦");

condition.await();

System.out.println("线程:"+Thread.currentThread().getName()+ " 叫醒啦");

}catch(InterruptedException e) {

System.out.println("抛异常啦");

}finally{

lock.unlock();

}

}

可以看到两种用法基本上是一致的,也就是将synchronized换成了lock,wait换成await,notify换成singal,

总结:

基本上我们平时用到的synchronized关键字的用法也就这些,但lock锁不一样,它还支持如上述的中断,更复杂的读写锁,还可以在aqs的基础上衍生出更多,如countDownLatch,cyclicBarrier等,可以支持我们做更多,但是不是lock就可以完全替代synchronized了呢,其实synchronized也有自己的优点,简单,不易出错,性能也不比lock差(有的书上写道synchronized性能比lock好,但其实就算好也不会好太多,对于我们来说,基本上可以忽略),真要说选哪个,我的建议是优先选synchronized,如果有特殊业务特殊需求synchronized无法满足,那当然是要用lock,不过,一定要记得释放锁哦。

本来打算结合reentrant和synchronized直接串起来讲的,但是确实有点多,这一篇就当作是后面的引子吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/434103.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

对cookie和子cookie操作的封装

1 /**2 * 封装cookie的操作3 * type {Object}4 */5 var CookieUtil {6 /**7 * 根据cookie的名字获取相应的值8 * param name cookie名字9 * return {*}10 */11 get:function (name) {12 //对name进行URL编码13 var cookieName…

解决Ubuntu下切换到root用户后没有声音问题

Ubuntu在root用户下,为了安全考虑默认是关闭了声音系统的。 如果要开很简单,因为root登录后pulseaudio没有启动。所以要先启动它 将root加到pulse和pulse-access组: sudo usermod -a -G pulse-access root gpasswd -a root pulse gpasswd -…

lua 给userdata设置元表_lua学习之复习汇总篇

第六日笔记1. 基础概念程序块定义在 lua 中任何一个源代码文件或在交互模式中输入的一行代码程序块可以是任意大小的程序块可以是一连串语句或一条命令也可由函数定义构成,一般将函数定义写在文件中,然后用解释器执行这个文件换行在代码中不起任何作用&a…

java代码请求2次_Android基于OkHttpUtils网络请求的二次封装

OkHttpUtils网络请求为什么进行二次封装?1、减少代码量2、后期换网络处理框架方便二次封装的实现原理1、将网络请求提取在一个方法中2、对里面的可变参数,可以通过参数传递过去,也可以提供一个set方法传递过去3、对于请求失败和成功,我们可以使用接口回调,让调用该方…

集群服务负载均衡------LVS

个人的理解,以一种通俗易懂的方式讲述出来,如果有哪些地方说的不正确的话,希望大家留言指出来。笔者会非是常的感谢! Cluster服务器集群,直接理解为一些单一的服务器的集合通过某种方式组合起来,为客户端提…

Cubieboard2 debian

环境准备 本文所使用的主机环境为kubuntu 12.10,然而一般情况下,下面涉及到的命令对基于Debian的(X)ubuntu系列都应该适用。 为不引起混淆,我们作如下约定: 工作目录为 $WORK_DIR,目标系统 rootfs 目录为 $WORK_DIR/$ROOTFS_DIR命…

linux和python的关系_Python、Linux与我的缘分

是在大二时期,那时候不懂什么技术,所以就选择了 Ubuntu 来学习、 使用, 它好操作、 界面绚丽、 简单易用, 对于我这种 Linux 新手来说知足了。 毕竟没玩过 Linux ,知识有限, 玩不转 Linux 的种种配置&#…

linux 磁盘uuid获取

ls -l /dev/disk/by-uuid/总用量 0lrwxrwxrwx 1 root root 10 2012-08-15 09:28 0af9bc87-c3c9-49eb-829e-caf572298cc7 -> http://www.cnblogs.com/sdb1lrwxrwxrwx 1 root root 10 2012-08-15 09:27 3e8b5c85-3f5b-4864-b45e-03ff0073eb5f -> http://www.cnblogs.com/sd…

tomcat jsp导入java_[导入]Tomcat JSP Web 开发中的乱码问题小姐

1. 静态页面的乱码问题文件的编码和浏览器要显示的编码不一致。1) 检查文件原始的编码, 可以用记事本打开, 然后选择另存为来看;2) 给当前页面加入一个指令来建议浏览器用指定的编码来显示文件字符内容.3) 如果系统是英文XP,没装东亚字符集支持, 也会显示乱码.2. JSP 页面的乱码…

编译Mysql 5.5时报do_abi_check错误

下载mysql-5.5.3-m3源码后&#xff0c;执行configure无错误&#xff0c;在make的时候却报: make[2]: *** [do_abi_check] 错误 1<br> make[2]: Leaving directory /tmp/mysql-5.5.3-m3<br> make[1]: *** [abi_check] 错误 2<br> make[1]: Leaving directory …

四大开源分布式存储_ipfs分布式存储行业面临着四大主要风险,你知道是哪些吗?...

为了响应国家号召、推动分布式存储技术落地、防御行业风险&#xff0c;中国分布式存储产业联盟启动&#xff0c;全国从事IPFS以及分布式存储从业者对行业风险及联盟成立的必要性达成了高度共识&#xff0c;目前有36家以上的IPFS分布式存储行业企业填写了联盟申请表。几位国内知…

mathematica打包java_从Mathematica到Java的图像

我试图从Mathematica中获取一张图片.我尝试评估一些使用包中的方法生成图形的Mathematica代码.如果我将代码粘贴到Mathematica Notebook,则会正确生成图形.所以我的问题&#xff1a;如何将这些图形转换为Java ???这是我的示例代码&#xff1a;ml MathLinkFactory.createKer…

pjsua帮助手册(中文)

原文地址 : http://www.pjsip.org/pjsua.htm 介绍 PJSUA是一个开源的命令行SIP用户代理&#xff08;软电话&#xff09;&#xff0c;用PJSIP协议&#xff0c;PJNATH&#xff0c;和PJMEDIA实现。 它虽然只有很简单的命令行界面&#xff0c;但是功能齐全。 SIP功能&#xff1a; 多…

c/c++笔试面试题(4)

c/c笔试面试题&#xff08;4&#xff09; 2007-11-08 16:46 749人阅读 评论(0) 收藏 举报Sony笔试题 1&#xff0e;完成下列程序 * *.*. *..*..*.. *...*...*...*... *....*....*....*....*.... *.....*.....*.....*.....*.....*..... *......*......*......*......*......*...…

js date转成 时间字符串_秋招快要开始了,前端笔试中的坑位-JS隐式转换问题

我们在写笔试题的时候&#xff0c;经常碰到涉及隐式转换的题目&#xff0c;例如"1" 2 obj 1 [] ![] [null] false 和 叫做严格运算符&#xff0c;对象类型指向地址相同或原始类型&#xff08; 数值、字符串、布尔值&#xff09;值相同&#xff1b;叫做相等运算…

Java中快速处理集合_简洁又快速地处理集合——Java8 Stream(上)

作者&#xff1a;Howie_Y&#xff0c;系原创投稿主页&#xff1a;www.jianshu.com/u/79638e5f0743Java 8 发布至今也已经好几年过去&#xff0c;如今 Java 也已经向 11 迈去&#xff0c;但是 Java 8 作出的改变可以说是革命性的&#xff0c;影响足够深远&#xff0c;学习 Java …

FTP服务器和客户端源代码编写问题(ftp server client source)

最近关注FTP程序源代码的朋友非常多&#xff0c;这里简单说明一下。 其实FTP也就是普通的Socket程序&#xff0c;只是需要按照FTP协议(RFC959, 1635?可能我记错了)去做&#xff0c;也就是每个消息有固定的结构的&#xff0c;比如头3个字节必须是200,201,300,400之类的数字表示…

eclipse编译java项目class文件_动态编译 Java 代码以及生成 Jar 文件

导读&#xff1a; 最近在看 Flink 源码的时候发现到一段实用的代码&#xff0c;该代码实现了 java 动态编译以及生成 jar 文件。将其进行改进后可以应用到我们的平台上&#xff0c;实现在平台页面上编写 java 代码语句&#xff0c;提交后由后台进行编译和打成 Jar 包再上传到指…

Android初级开发第七讲--特效和数据传递处理

博客出自&#xff1a;http://blog.csdn.net/liuxian13183&#xff0c;转载注明出处&#xff01; All Rights Reserved ! 大家好&#xff0c;相信大家对iphone上的特效早有耳闻&#xff0c;特效不仅给人以炫丽的感觉&#xff0c;也给人以性能优越的感觉&#xff1b;但万丈高楼平…

java查看日志命令_[Java教程]【Linux】linux查看日志文件内容命令tail、cat、tac、head、echo...

[Java教程]【Linux】linux查看日志文件内容命令tail、cat、tac、head、echo0 2017-11-14 12:00:29linux查看日志文件内容命令tail、cat、tac、head、echotail -f test.log你会看到屏幕不断有内容被打印出来. 这时候中断第一个进程Ctrl-C,---------------------------linux 如何…