- 浏览: 1682521 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (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 740简介 在之前的文章里我们讨论过一些程序构建Pro ... -
java annotation基础
2018-06-30 16:41 887简介 之前有一篇简短的文章讨论过annotati ... -
spring源代码分析:annotation支持的实现
2018-09-03 23:31 2560简介 在之前的文章里,我们讨论了spring I ... -
spring container 实现分析:BeanFactory and ApplicationContext
2018-06-02 18:29 1473简介 在之前的文章里,我们讨论过作为spring ... -
spring aop: ProxyFactory
2018-04-30 16:45 842简介 在之前的文 ... -
日志与log4j2的配置和应用总结
2018-02-15 15:47 12322简介 通常我们在日常的应用开发中,日志起到一个非 ... -
Java servlet学习总结
2018-01-02 21:14 0简介 应用系统架构的演化(从CS到BS) ... -
spring container 实现分析:BeanWrapper
2018-02-19 18:10 1985简介 在之前的一篇文章里, 我们基于《Exper ... -
spring propertyeditor
2017-10-26 09:17 0pro spring 5 chapter 4, page ... -
spring bean life cycle
2018-02-25 13:30 931简介 在使用spring bean的过程中,有一个 ... -
spring container的设计与实现分析
2017-10-08 21:31 2779简介 在之前的一 ... -
jdbc数据访问的封装和演化
2017-09-16 17:00 2694简介 在使用传统jdbc的方式来访问数据的时候, ... -
Boyer Moore算法分析总结
2017-03-31 18:42 3558简介 在之前的文章里,对于字符串的搜索算法,我曾 ... -
mybatis学习总结:mybatis和spring, spring boot的集成
2017-03-04 18:07 2513简介 在前面的讨论里已经提到了mybatis的各种配置 ... -
mybatis学习总结:annotation与xml结合示例
2017-02-25 21:09 3710简介 在之前的文章里讨论过mybatis纯xml或者 ... -
mybatis学习总结:对象关系映射的xml配置实现
2017-02-19 23:03 4068简介 在之前的文章里已经讨论过mybatis的基本配 ... -
mybatis学习总结:annotation示例改进
2017-01-24 09:06 3445简介 在前一篇文 ... -
mybatis学习总结:基础示例
2017-01-21 23:30 901简介 mybatis是一个比较流行的ORM框架, ... -
gradle学习总结
2016-12-18 21:01 4633简介 Java的自动化构建工具比较多,从最开始的an ... -
String sort的几种方法
2016-10-16 23:07 2192简介 在之前的一 ...
相关推荐
书中会讲解监视器(Monitor)、锁(Lock)、条件变量(Condition)以及它们在`synchronized`关键字中的应用。 3. **线程安全**:线程安全的类或方法能够正确处理多线程环境下的并发访问。书中讨论了线程安全的层次...
本教程"JavaConcurrency:Java并发教程"旨在深入探讨Java平台上的并发机制和最佳实践。 Java并发的核心概念包括线程、同步、互斥、死锁以及线程安全。在Java中,线程是并发执行的基本单元,通过创建Thread对象或者...
《Java Concurrency in Practice》是Java并发编程领域的一本经典著作,由Brian Goetz、Tim Peierls、Joshua Bloch、Joe Bowbeer、David Holmes和Doug Lea合著,国内有热心人士进行了中文翻译,使得更多的中国开发者...
3. **线程同步**:为避免多个线程访问共享资源时产生的数据不一致问题,Java提供了多种同步机制,如`synchronized`关键字、`Lock`接口(包括`ReentrantLock`)、`Semaphore`信号量等。 4. **死锁**:当两个或更多...
Java提供了多种同步工具,包括`synchronized`关键字、`Lock`接口(如`ReentrantLock`)、`Semaphore`信号量、`CountDownLatch`倒计时器以及`CyclicBarrier`回环栅栏。`synchronized`关键字可以用于方法或代码块,...
本文主要讲解了Java中的两种锁类型:同步锁和JUC(Java Util Concurrency)包中的锁。 1. 同步锁(Synchronized) 同步锁是Java 1.0就引入的原生锁机制,主要通过`synchronized`关键字实现。它的核心原则是互斥性,...
《Java Concurrency in Practice》是Java并发编程领域的一本经典著作,由Brian Goetz、Tim Peierls、Joshua Bloch、Joseph Bowles和Doug Lea等专家共同编写,于2006年出版。这本书深入浅出地探讨了Java平台上的多...
避免死锁的方法包括避免嵌套锁、使用定时锁(如`java.util.concurrent.locks.Lock`)和避免持有多个锁。 3. **线程通信**:Java提供了`wait()`, `notify()`和`notifyAll()`方法来实现线程间的通信。这些方法必须在`...
本文将深入探讨Java中的锁机制,主要包括同步锁(synchronized)和Java并发工具包(Java Concurrency Utilities, JUC)中的锁。 同步锁是Java 1.0版本就引入的机制,主要通过`synchronized`关键字实现。它确保对...
3. **同步机制**:Java提供了多种同步机制,包括synchronized关键字、java.util.concurrent.locks包下的Lock接口及其实现,以及java.util.concurrent.atomic包中的原子变量类。这些机制有助于控制并发访问,确保线程...
Java并发编程学习笔记 这是我两年前(2012-05)的学习笔记。。 -- 本文不会详细介绍java5以前的多线程开发...协调共享对象访问,在java5以前用synchronized实现,现在可以用Lock显式的lock()和unlock(),并且有定时锁,读写
- Lock接口与ReentrantLock类:比synchronized更灵活的锁机制,提供公平锁和非公平锁,以及可中断和可尝试获取锁的能力。 3. **并发工具类** - Executor框架:通过ExecutorService、ThreadPoolExecutor和...
5. **同步机制**:除了`synchronized`,Java还提供了`Lock`接口及其实现,如`ReentrantLock`,提供了比`synchronized`更灵活的锁定机制,包括公平锁和非公平锁、可中断锁和定时锁。 6. **并发集合**:Java并发库...
- `java.util.concurrent.locks.Lock`接口提供了比`synchronized`更细粒度的锁控制,如可重入锁(`ReentrantLock`)、公平锁、读写锁等。 - `ReentrantLock`支持公平性和非公平性,同时具有与`synchronized`类似的...
- **Lock** 接口与实现:如`ReentrantLock`,提供了比`synchronized`更细粒度的锁控制。 4. **并发集合** - **ConcurrentHashMap**:线程安全的哈希映射,提供了高并发性能。 - **CopyOnWriteArrayList** 和 **...
`ReentrantLock`提供了比Java内置的`synchronized`关键字更丰富的功能和更高的控制级别。它具有可重入性,意味着一个线程可以多次获取同一锁,这在递归方法或嵌套同步块中特别有用。`ReentrantLock`有两种模式:公平...
2. **线程同步**:在多线程环境下,数据共享可能导致数据不一致问题,Java提供了多种同步机制,如`synchronized`关键字、`Lock`接口(包括`ReentrantLock`)以及`volatile`关键字,以确保线程安全。 3. **并发集合*...
- `Lock`接口和`ReentrantLock`类:提供比`synchronized`更细粒度的锁控制,如公平锁、非公平锁、读写锁等。 5. **并发工具类**: - `java.util.concurrent`包提供了丰富的并发工具,如`ExecutorService`、`...
在Java并发编程中,`Condition`接口是Java并发包(java.util.concurrent)中的一个重要组成部分,它提供了比`synchronized`关键字更为精细的线程同步和唤醒机制。`Condition`允许我们创建多个独立的等待队列,每个...
《Java Concurrency in Practice》是Java并发编程领域的一本经典著作,由Brian Goetz、Tim Peierls、Joshua Bloch、David Holmes和Doug Lea合著。这本书深入浅出地探讨了Java平台上的多线程和并发编程,提供了丰富的...