关于“单例同步”:
一直有人在问单例对象的并发调用是否需要同步,基本属于“月经帖”了,答案是现成的满天下都是,但真
正能让人心里踏实下来的解释寥寥无几。实际上,只要学习了一些JVM的运行原理,解释这个问题就不难了。
如果一个类是单例的,比如某些DAO的设计,那么所有的线程来访问这个类的实例的时候,它们获得的都将
是同一个对象,这是不言自明的。如果这些线程的当前操作是“互斥”的,那么每个线程就必须在取得该实例
的访问资格的时候为该对象上锁,以独享该对象直到当前操作结束,以免在操作中途被其它线程介入而产生不
可预知的结果。问题是,什么样的操作是“互斥”的呢?
简单地说,互斥操作就是两个操作企图对它俩共享的某个资源进行修改,而修改的结果是不可预知的。于是
问题就变成了,什么才是“共享的资源”?从纯粹java语法的角度这个问题没法解释,因为它遵循的是当前java
虚拟机的规范描述。现假设两个线程正企图同时访问一个单例对象的方法,如,
int method1(int i) {
int j = 3;
return i+j;
}
一个规范的虚拟机线程在调用method1()的时候是这样做的:
1) 把method1()的局部变量,包括参数,压入当前线程的栈;
2) 从当前线程栈弹出变量j,并赋予数值3;
3) 从当前线程栈弹出参数i,与j执行加法运算;
4) 从当前线程栈中释放当前方法占用的栈帧,并把method1()的结果压入当前线程栈。
需要说明的是,当前线程栈是当前线程独有的,绝对不会被其它线程访问到。这样,只要你在method1()里面
使用的全都是局部变量或参数,那就不需要为多线程的并发调用发愁,因为每个线程都有自己的栈帧,各不相
干。
复杂一点,如果method1()是这样定义的:
int method1(int i, SingletonClass singleObj) {
singleObj.intValue ++;
int j = i + singleObj.intValue;
一个规范的虚拟机线程在调用method1()的时候是这样做的:
1) 把method1()的局部变量,包括参数,压入当前线程的栈;
2) 从当前线程栈弹出变量j,并赋予数值3;
3) 从当前线程栈弹出参数i,与j执行加法运算;
4) 从当前线程栈中释放当前方法占用的栈帧,并把method1()的结果压入当前线程栈。
需要说明的是,当前线程栈是当前线程独有的,绝对不会被其它线程访问到。这样,只要你在method1()里面
使用的全都是局部变量或参数,那就不需要为多线程的并发调用发愁,因为每个线程都有自己的栈帧,各不相
干。
复杂一点,如果method1()是这样定义的:
int method1(int i, SingletonClass singleObj) {
singleObj.intValue ++;
int j = i + singleObj.intValue;
return j;
}
这下我们就不得不考虑线程同步问题了,这个方法显然包含了一个互斥的操作“singleObj.intValue ++;”。
前面说过,方法的参数会被压入当前线程私有的栈直到方法结束,但这里要注意的是,singleObj只是一个引用
地址而非真正的对象实例,因此,尽管singleObj这个引用值是被压入线程私有栈去的,但真正的对象实例却是
在堆里存放的,栈虽然是线程私有的,堆却是所有线程共享的,因此singleObj的成员变量intValue是完全有可
能在当前线程执行第二行代码前被其它线程修改了的。比如说,线程1调用mothod1()的时候
singleObj.intValue的值是1, i的值是2,那么正确的情况下,method1()的返回值应该是4。但当线程1和线程
2几乎同时调用method1(),线程2恰好在线程1把intValue变成2之后的一瞬间又执行了一次
singleObj.intValue ++,由于singleObj是单例,两个线程遇到的singleObj是同一个对象,因此这次运算将把
intValue变成3。接下来线程1继续第二行代码,结果j的结果变成了i+3 = 2+3 = 5 。 如此一来,线程1调用
method1()的返回结果究竟会是 4 还是 5 是无法确定的,只能凭运气,寄望线程2在线程1从调用method1()到
取得返回值之间的这段时间打盹。在绝大多数情况下,这种“凭运气”的做法是不能接受的,我们需要向线程1
保证,在它调用method1()期间绝不会收到线程2的干扰。做法如下:
int method1(int i, SingletonClass singleObj) {
int j = 0;
synchronize(singleObj) {
singleObj.intValue ++;
j = i + singleObj.intValue;
}
return j;
}
这个写法仍然有缺陷,因为线程2很可能在线程1执行int j = 0 的时候修改singleObj的intValue,所以比较可靠
的应该在调用method1()之前锁住singleObj:
synchronize(singleObj) {
int result = obj.method1(2, singleObj);
}
小小总结一下,“一个方法如果涉及对某个共享对象(或堆对象)的写操作,那么它必须同步该对象”这个说
法在大多数情况下都对,但还有些失之笼统,或许这样说比较准确些,“如果一个方法对某共享对象的写操作
会造成其它线程返回值的不确定性,则该方法应该同步该对象。”
分享到:
相关推荐
### Java分布式应用学习笔记03:JVM对线程的资源同步和交互机制 在深入探讨Java虚拟机(JVM)如何处理线程间的资源同步与交互机制之前,我们先来明确几个关键概念:线程、多线程、同步、并发以及它们在Java中的实现...
11. **线程并发**:JVM如何支持多线程,包括线程同步机制如synchronized、Lock等,以及线程池的使用和优化。 通过观看"jvm视频",你可以直观地了解这些概念,并通过"jvm笔记"加深理解和记忆。理论学习后,实践操作...
本文将深入探讨JVM中的访问控制器,主要基于“java之jvm学习笔记十一(访问控制器)-源码”这一主题,以及相关的源码分析。 首先,我们得了解Java的安全模型。Java安全模型基于一种称为安全管理器(SecurityManager)...
这份“JVM的学习笔记PDF版”应该包含了关于JVM的详细信息,帮助学习者深入理解这个复杂的系统。JVM允许Java代码跨平台运行,通过解释器、类加载器、垃圾收集器等组件实现“一次编写,到处运行”的理念。 1. **JVM...
在本压缩包“bjy学习笔记-阿里巴巴Java编码规范以及一些技术笔记”中,我们可以预见到包含的内容主要是关于Java编程语言的学习心得,特别是遵循阿里巴巴的Java编码规范的相关知识。这是一份宝贵的资源,对于Java...
《良葛格 Java 学习笔记-JavaGossip全(v1+v2)》是一部集成了作者良葛格多年编程经验的学习资料,旨在帮助初学者和有经验的开发者深入理解和掌握Java这门强大的编程语言。这份笔记包含了JavaGossip的两个版本,v1和...
- **线程与进程**:线程的概念、状态转换、同步与互斥,以及线程池的使用。 - **并发工具类**:如Semaphore、CountDownLatch、CyclicBarrier、ThreadPoolExecutor等的使用。 - **并发原语**:synchronized、...
今天,我们将要学习Java多线程编程的基础知识,包括多线程原理、线程状态、线程同步等内容。 一、多线程原理 多线程编程的原理是指在一个进程中可以同时执行多个线程,每个线程都可以独立地执行不同的任务。Java...
MySQL是世界上最受欢迎的关系型数据库管理系统之一,用于存储和管理数据。...通过这些笔记,你可以系统地学习和理解MySQL数据库、Java并发编程以及JVM的运作机制,为你的软件开发职业生涯打下坚实的基础。
本文将基于文档《Java并发编程与高并发解决方案-学习笔记***.pdf》中提供的内容,来详细阐述并发编程和高并发的基本概念、CPU多级缓存与缓存一致性、以及Java内存模型。 ### 并发与高并发概念 在现代多线程编程中...
在JVM的学习中,理解其内存模型、垃圾收集算法以及类加载机制至关重要。 1. **JVM内存模型** - **方法区**:也称为“永久代”,存储虚拟机加载的类信息、常量、静态变量等,是线程共享的区域。在Java 8之后,这...
同时,学习Java的并发编程,包括线程、同步机制(synchronized关键字和Lock接口)以及并发集合(如ConcurrentHashMap),能帮助你编写出多线程环境下的高效程序。 最后,Java的异常处理、垃圾回收机制以及内存管理...
Java编程语言是面向对象的、跨平台...这些只是Java编程中的一部分知识点,实际的Java学习过程中,还需要深入理解反射、注解、并发编程、JVM原理、设计模式等多个方面。不断实践和学习,才能成为一名优秀的Java开发者。
- `synchronized`修饰的方法确保线程同步访问。 - **synchronized关键字**: - 编译结果展示了`synchronized`的关键作用。 - **monitorenter和monitorexit**: - JVM层面实现`synchronized`的关键字。 #### 十、...
本学习笔记将深入探讨Java多线程的相关知识,包括其原理、实现方式、同步机制以及常见问题。 ### 一、多线程的基本概念 多线程是指在一个程序中存在两个或更多的执行线程,这些线程共享同一内存空间,但各自拥有...
### Java多线程学习笔记 #### 一、线程的基本概念 在计算机科学中,**线程**(Thread)是程序执行流的最小单位。一个标准的程序只能做一件事情,而通过多线程技术,可以让程序同时处理多个任务。在Java中,线程是...
- **多线程**:Java内置对多线程的支持,包括线程的创建、同步、互斥等机制。 - **Swing与AWT**:Java的图形用户界面(GUI)开发库,用于创建桌面应用程序。 3. **JavaEE(企业版)** - **Servlet与JSP**:在...
线程同步机制包括synchronized关键字、wait()、notify()和notifyAll()方法,以及Lock接口和相关的实现类。 8. 数据库操作:Java JDBC(Java Database Connectivity)允许Java程序与各种数据库交互。它提供...
学习Java多线程编程不仅涉及到线程的创建和启动,还包括线程同步(如`synchronized`关键字、`wait()`, `notify()`, `notifyAll()`方法)、线程间通信(`BlockingQueue`、`Future`、`ExecutorService`)、线程池(`...