- 浏览: 1669303 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (335)
- uml (11)
- java (318)
- python (11)
- socket (2)
- ant (1)
- data structures (58)
- algorithms (67)
- concurrency (16)
- multithreading (16)
- mysql (2)
- ubuntu (2)
- c语言 (1)
- lucene (0)
- elasticsearch (0)
- django (1)
- 读书 观点 (2)
- 读书 (4)
- 观点 (4)
- collections (8)
- nio (1)
- io (2)
- 系统集成 (1)
- activemq (2)
- restful (0)
- web service (0)
- HttpClient (1)
- serializable (0)
- annotation (1)
- jdbc (3)
- classloader (0)
- regular expression (1)
- jpa (4)
- jvm (0)
- reflection (1)
- commons-pool2 (2)
- javamail (1)
- velocity (1)
- mathematics (3)
- graph (13)
- LeetCode (159)
- scala (0)
- spring (24)
- maven (5)
- spring batch (1)
- quartz (2)
- IOC (2)
- ORM (3)
- hibernate (2)
- aop (4)
- redis (0)
- zookeeper (0)
- spring mvc (4)
- ELK (1)
- string (1)
- gradle (1)
- mybatis (5)
- docker (0)
- servlet (0)
- log4j2 (1)
- html (0)
- css (0)
最新评论
-
nucleus:
貌似是因为图片的路径是http的缘故:http://dl2.i ...
spring container 实现分析:BeanWrapper -
nucleus:
nucleus 写道BeanWrapper这一块相关的类结构如 ...
spring container 实现分析:BeanWrapper -
nucleus:
BeanWrapper这一块相关的类结构如下图:文中提到的上述 ...
spring container 实现分析:BeanWrapper -
leshy:
inventory.sort(Comparator.compa ...
java8 lambda表达式学习总结 -
Leisurez:
di1984HIT 写道xuexile~hahaha~
activemq的几种基本通信方式总结
简介
在java的多线程编程中,需要考虑的最多的情况莫过于线程之间的同步和通信了。在线程的同步机制中,最常用的莫过于synchronized和lock。从更深层次的比较来说,他们有什么特点呢?在开发的时候到底哪种方式比较合适?我们就详细的了解一下吧。
synchronized简介
一提起synchronized,似乎太简单了。在任何需要多线程访问的情况下,如果要对所访问的数据进行限制,保证每次只有一个线程可以操作该数据,我们可以在数据或者方法部分加一个synchronized。synchronized主要有两种使用的方式,一种是在一个方法内部用synchronized封装的代码块,可以称之为synchronized声明,还有一种是synchronized方法,主要是用于修饰一个方法。
两者的使用方式分别如下:
synchronized声明:
synchronized(lockObject) { // put your stuff here }
synchronized方法:
synchronized void doSomething() { // Here is the business code. }
光看这两种使用方式,似乎太简单了,没什么好说的。在更深层次里,jvm用了一些特别的手法来实现synchronized的特性。
更进一步分析
Monitor
synchronized在jvm内部的实现是通过一种monitor的机制。synchronized在编译后会生成monitorenter和monitorexit这两个字节码。这两个字节码都需要一个引用类型的参数来指定要加锁和解锁的对象。如果synchronized指明了参数对象,也就是采用synchronized声明的方式,则该对象就是要加锁和后续解锁的对象。如果synchronized修饰的是方法,则根据方法对应的对象或者类来获取加锁和解锁对象。如果该方法是某个对象的,则对对象进行操作,如果是类方法,则获取该方法所在类的Class对象。
系统生成的monitorenter和monitorexit正好封装了我们要同步操作的那部分代码块。
我们来看一个示例,分别采用synchronized声明和synchronized方法实现。
下面是synchronized方法实现的代码:
class Prompter { int delay; Prompter(int d) { if(d <= 0) d = 1; delay = d; } synchronized void display(String msg) { for(int i = 0; i < msg.length(); i++) { System.out.print(msg.charAt(i)); if(Character.isWhitespace(msg.charAt(i))) { try { Thread.sleep(delay * 1000); } catch(InterruptedException exc) { return; } } } System.out.println(); } } class UsePrompter implements Runnable { Prompter prompter; String message; UsePrompter(Prompter p, String msg) { prompter = p; message = msg; new Thread(this).start(); } public void run() { prompter.display(message); } } class SyncDemo { public static void main(String[] args) { Prompter p = new Prompter(1); UsePrompter promptA = new UsePrompter(p, "One Two Three Four"); UsePrompter promptB = new UsePrompter(p, "Left Right Up Down"); } }
我们定义了设定同步的地方在Prompter的display方法。两个线程分别调用Prompter对象的display方法。这时,如果我们反编译生成的Prompter class文件,会生成如下的结果:
Compiled from "SyncDemo.java" class Prompter { int delay; Prompter(int); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: iload_1 5: ifgt 10 8: iconst_1 9: istore_1 10: aload_0 11: iload_1 12: putfield #2 // Field delay:I 15: return synchronized void display(java.lang.String); Code: 0: iconst_0 1: istore_2 2: iload_2 3: aload_1 4: invokevirtual #3 // Method java/lang/String.length:()I 7: if_icmpge 55 10: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 13: aload_1 14: iload_2 15: invokevirtual #5 // Method java/lang/String.charAt:(I)C 18: invokevirtual #6 // Method java/io/PrintStream.print:(C)V 21: aload_1 22: iload_2 23: invokevirtual #5 // Method java/lang/String.charAt:(I)C 26: invokestatic #7 // Method java/lang/Character.isWhitespace:(C)Z 29: ifeq 49 32: aload_0 33: getfield #2 // Field delay:I 36: sipush 1000 39: imul 40: i2l 41: invokestatic #8 // Method java/lang/Thread.sleep:(J)V 44: goto 49 47: astore_3 48: return 49: iinc 2, 1 52: goto 2 55: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 58: invokevirtual #10 // Method java/io/PrintStream.println:()V 61: return Exception table: from to target type 32 44 47 Class java/lang/InterruptedException }
现在,我们再看看另外一个synchronized声明的版本的代码:
class Prompter { int delay; Prompter(int d) { if(d <= 0) d = 1; delay = d; } public void display(String msg) { synchronized(this) { for(int i = 0; i < msg.length(); i++) { System.out.print(msg.charAt(i)); if(Character.isWhitespace(msg.charAt(i))) { try { Thread.sleep(delay * 1000); } catch(InterruptedException exc) { return; } } } System.out.println(); } } } class UsePrompter implements Runnable { Prompter prompter; String message; UsePrompter(Prompter p, String msg) { prompter = p; message = msg; new Thread(this).start(); } public void run() { prompter.display(message); } } class SyncDemo { public static void main(String[] args) { Prompter p = new Prompter(1); UsePrompter promptA = new UsePrompter(p, "One Two Three Four"); UsePrompter promptB = new UsePrompter(p, "Left Right Up Down"); } }
代码几乎和前面的一样,唯一的差别就是将原来的synchronized display方法修改成了synchronized(this)的声明。这样,我们相当于对Prompter对象加锁。
再看看对Prompter class反编译的结果:
Compiled from "SyncDemo.java" class Prompter { int delay; Prompter(int); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: iload_1 5: ifgt 10 8: iconst_1 9: istore_1 10: aload_0 11: iload_1 12: putfield #2 // Field delay:I 15: return public void display(java.lang.String); Code: 0: aload_0 1: dup 2: astore_2 3: monitorenter 4: iconst_0 5: istore_3 6: iload_3 7: aload_1 8: invokevirtual #3 // Method java/lang/String.length:()I 11: if_icmpge 62 14: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 17: aload_1 18: iload_3 19: invokevirtual #5 // Method java/lang/String.charAt:(I)C 22: invokevirtual #6 // Method java/io/PrintStream.print:(C)V 25: aload_1 26: iload_3 27: invokevirtual #5 // Method java/lang/String.charAt:(I)C 30: invokestatic #7 // Method java/lang/Character.isWhitespace:(C)Z 33: ifeq 56 36: aload_0 37: getfield #2 // Field delay:I 40: sipush 1000 43: imul 44: i2l 45: invokestatic #8 // Method java/lang/Thread.sleep:(J)V 48: goto 56 51: astore 4 53: aload_2 54: monitorexit 55: return 56: iinc 3, 1 59: goto 6 62: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 65: invokevirtual #10 // Method java/io/PrintStream.println:()V 68: aload_2 69: monitorexit 70: goto 80 73: astore 5 75: aload_2 76: monitorexit 77: aload 5 79: athrow 80: return Exception table: from to target type 36 48 51 Class java/lang/InterruptedException 4 55 73 any 56 70 73 any 73 77 73 any }
如果我们仔细去看两种方式反编译后的结果,会发现除了synchronized声明中增加了一个monitorenter和monitorexit外,几乎没什么差别。两者有这么一个细微的差别是在于对于synchronized方法,jvm自动帮我们去获取绑定该方法的对象锁了,而对于我们指定的对象,jvm会生成monitorenter和monitorexit的字节码。
可重入
jvm编译后的monitor还有一个很重要的特性就是支持可重入。它表示,在执行monitorenter指令的时候,首先要去尝试获取对象的锁,如果这个对象没被锁定,或者当前线程已经拥有了那个对象的锁,把所的计数器加1.在执行monitorexit时将锁的计数器减1,当计数器为0时,锁就被释放了。这样一个好处就是如果当前获取到锁的线程它再次去访问到该锁锁定的部分时可以直接方法,只需要对锁计数器加1,而不至于还要被阻塞。它这种可重入性有一个典型的好处,见如下代码:
public class Super { public synchronized void doSomething() { ... } } public class SubClass extends Super { public synchronized void doSomething() { System.out.println(toString() + ": calling something"); super.doSomething(); } }在这里,父类和子类都同步限制了doSomething方法。子类要访问父类的doSomething方法。如果锁不是可重入的话,要调用父类的方法就会被阻塞。而且,要解决这个问题也会比较麻烦。
lock
java.util.concurrent.lock
中的 Lock
框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。这就为 Lock
的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。 ReentrantLock
类实现了Lock
,它拥有与 synchronized
相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。)
ReentrantLock类的使用方法如下所示:
Lock lock = new ReentrantLock(); lock.lock(); try { // update object state } finally { lock.unlock(); }我们如果要使用ReentrantLock类,必须要用try,finally块的方式来使用,在try里面更新数据,在finally里面释放锁。
除了上面的那些差别,ReentrantLock的加锁机制也和synchronized几乎一样,它也是可重入的,通过同样的计数器机制来获取和释放锁。
两者的比较
一个非常常见的说法就是,Lock实现了synchronized的所有功能,同时提供了更加高级和更加细粒度的控制。比如ReentrantLock就有如下几项:等待可中断、可实现公平锁,以及锁可以绑定多个条件。从以往的比较来看,ReentrantLock的性能比较synchronized相对要好一些。随着JDK6以及后续一些版本的优化,他们的差别已经很小了。至少从官方来看还是比较倾向于使用synchronized。
参考资料
Java 理论与实践: JDK 5.0 中更灵活、更具可伸缩性的锁定机制
发表评论
-
spring源代码分析:aop的实现
2018-10-03 23:32 694简介 在之前的文章里我们讨论过一些程序构建Pro ... -
java annotation基础
2018-06-30 16:41 839简介 之前有一篇简短的文章讨论过annotati ... -
spring源代码分析:annotation支持的实现
2018-09-03 23:31 2468简介 在之前的文章里,我们讨论了spring I ... -
spring container 实现分析:BeanFactory and ApplicationContext
2018-06-02 18:29 1412简介 在之前的文章里,我们讨论过作为spring ... -
spring aop: ProxyFactory
2018-04-30 16:45 794简介 在之前的文 ... -
日志与log4j2的配置和应用总结
2018-02-15 15:47 12216简介 通常我们在日常的应用开发中,日志起到一个非 ... -
Java servlet学习总结
2018-01-02 21:14 0简介 应用系统架构的演化(从CS到BS) ... -
spring container 实现分析:BeanWrapper
2018-02-19 18:10 1878简介 在之前的一篇文章里, 我们基于《Exper ... -
spring propertyeditor
2017-10-26 09:17 0pro spring 5 chapter 4, page ... -
spring bean life cycle
2018-02-25 13:30 881简介 在使用spring bean的过程中,有一个 ... -
spring container的设计与实现分析
2017-10-08 21:31 2691简介 在之前的一 ... -
jdbc数据访问的封装和演化
2017-09-16 17:00 2610简介 在使用传统jdbc的方式来访问数据的时候, ... -
Boyer Moore算法分析总结
2017-03-31 18:42 3494简介 在之前的文章里,对于字符串的搜索算法,我曾 ... -
mybatis学习总结:mybatis和spring, spring boot的集成
2017-03-04 18:07 2473简介 在前面的讨论里已经提到了mybatis的各种配置 ... -
mybatis学习总结:annotation与xml结合示例
2017-02-25 21:09 3660简介 在之前的文章里讨论过mybatis纯xml或者 ... -
mybatis学习总结:对象关系映射的xml配置实现
2017-02-19 23:03 4023简介 在之前的文章里已经讨论过mybatis的基本配 ... -
mybatis学习总结:annotation示例改进
2017-01-24 09:06 3392简介 在前一篇文 ... -
mybatis学习总结:基础示例
2017-01-21 23:30 853简介 mybatis是一个比较流行的ORM框架, ... -
gradle学习总结
2016-12-18 21:01 4540简介 Java的自动化构建工具比较多,从最开始的an ... -
String sort的几种方法
2016-10-16 23:07 2127简介 在之前的一 ...
相关推荐
concurrency:Java Concurrency in Practice源代码
Java Concurrency in Practice 英文无水印pdf pdf所有页面使用FoxitReader和PDF-XChangeViewer测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者...
The Art of Concurrency: A Thread Monkey's Guide to Writing Parallel Applications PDF文件,很清晰,,, Intel资深工程师分享并发编程知识及内幕技术 详述如何在各种算法中使用线程化方法 开发并行代码算法...
Java Concurrency in practice
Using the concurrency building blocks in java.util.concurrent Performance optimization dos and don'ts Testing concurrent programs Advanced topics such as atomic variables, nonblocking algorithms, ...
<<java并行编程>>英文版chm格式,英文名称<Java Concurrency in Practice>,一直想买这本书,但总是缺货,找到了电子版,分享给大家。 Java Concurrency in Practice By Brian Goetz, Tim Peierls, Joshua Bloch,...
java-concurrency:代码审查清单
Java Concurrency Programming Lessons.
java concurrency in practice
Java concurrency in Practice高清pdf,带目录标签,Java并发实战
Java Concurrency in Practice JAVA并发编程实践中文版(全)第二部分
介绍java多线程的超经典的书,不需要多介绍了。这是完整版。压缩包内包含两个版本的pdf,都是英文版。建议看英文版,因此中文版翻译得超烂。与此同时,压缩包内还有一个java多线程总结.ppt!
Java Concurrency in Practice源码
Get an easy introduction to reactive streams in Java to handle concurrency, data streams, and the propagation of change in today's applications. This compact book includes in-depth introductions to ...
正规PDF版本的 Java Concurrency In Practice。 经典著作,学习有益!
Java并发 Java并发教程
java concurrency programming
Java.Concurrency.in.Practice.pdf