那么我们现在来看前面的第四个疑问:对方法加 synchronized 关键字与用 synchronized(xxx) 同步代码块两种规避方法又有什么分别和联系呢?
如果您是一路读下来的,就很清楚,synchronized(xxx) 是把危险的代码同步起来,即框起来,使之同时只能一个线程执行这块代码(监视区域),并为该代码块关联一个对象。不妨从字节码的角度来分析下,比如我们来看看 operate() 方法产生的字节码
代码为:
public void operate() {
synchronized(this){
flag ++;
flag --;
}
}
字节码如下:
public void operate();
Code:
0: aload_0 //位置为0的局部变量,即方法的第一个参数 this 压栈
1: dup //复制栈顶,栈顶仍然为 this,栈出口是连续两个 this
2: astore_1 // 弹出栈顶的 this,存到位置为 1 的局部变量
3: monitorenter //进入监视区,弹出栈顶(还是 this),并对 this 加锁
4: getstatic #17; //Field flag:I
7: iconst_1
8: iadd
9: putstatic #17; //Field flag:I
12: getstatic #17; //Field flag:I
15: iconst_1
16: isub
17: putstatic #17; //Field flag:I
20: aload_1 //位置为 1 的局部变量(记得吧,还是 this) 压栈 -- 正常退出方法
21: monitorexit //退出监视区域,弹出栈顶(this),并释放 this 锁
22: goto 28
25: aload_1 //位置为 1 的局部变量(记得吧,还是 this) 压栈 -- 异常退出方法
26: monitorexit //退出监视区域,弹出栈顶(this),并释放 this 锁
27: athrow
28: return
Exception table: //字节码里加了异常表,在碰到任何异常都能释放对象锁
from to target type
4 22 25 any
25 27 25 any
4 到 17 是正常的方法体部分,用 synchronized(xxx) 来同步代码块会使用 monitoenter...monitorexit 制造一个监视区域,该监视区域会与栈顶的对象进行关联。这里用的是 this,如果你写成的是 synchronized(object),那么该监视区域则会与 object 关联。附:在 C++ 中分别用 EnterCriticalSection() 和 LeaveCriticalSection() 方法来进入和离开临界区代码的。
直接给方法加个 synchronized 关键字(public synchronized void operate() {......}) 会有什么功效呢?只要稍稍发挥一下想像力,既然 synchronized(xxx) 是给代码块做了个监视区,并与 xxx 对象关联,那么给方法加个关键字就应该是把方法体的所有代码行放到监视区域了。我们说监视区域总是会与某一个对象相关联,然而方法加 synchronized 关键要与什么隐式对象关联,我们有如下规则:
1) 如果是非静态的同步方法,关联对象就是 this,相当于 synchronized(this) 括起了方法所有代码
2) 如果是静态的同步方法,方法无法访问到 this(不存在),此时关联对象就是该类的 Class 实例,比如对于 TestMultiThread 就相当于用 synchronized(TestMultiThread.class) 括起了方法所有代码
当你看到第一条规则,非静态方法,加上 synchronized 关键字也就相当于 synchronized(this),是不是也意识到了单纯给方法加个关键字 synchronized 有时候也解决不了问题,何不亲手把上面的 operate() 方法写成如下:
01.public synchronized void operate() {
02. flag++;
03. try {
04. // 增加随机性,让不同线程能在此交替执行
05. Thread.sleep(new Random().nextInt(5));
06. } catch (InterruptedException e) {
07. e.printStackTrace();
08. }
09. flag--;
10. System.out.println("Thread: " + Thread.currentThread().getName()
11. + " /Current flag: " + flag);
12.}
执行下 TestMultiThread 程序看看,你仍然会得到这个你不想要的结果:
Thread: Thread-01 /Current flag: 2
Thread: Thread-02 /Current flag: 1
要如何加个小小的改造呢?对了让该方法是静态的 public static synchronized void operate(),就每次都能输出为 1 的 flag 值了,因为它是与 TestMultiThread.class 进行关联了。
再进一步思考,很多事不能想当然的,不然就会出现 出生入死--一出生就去死,死于非命--死的不是命那样的解释了。为方法加 synchronized 关键字,会不是就用 monitorenter 和 monitorexit 框了所有代码呢?逻辑上确实说的过去,但事实上加个 synchronized 关键字只会在字节码中 method_info 表的该方法上加上一个存取标志(access_flag) ACC_SYNCHRONIZED(0x0020),不会在原方法指令中插入 monitorenter 和 monitorexit,JVM 知道怎么去处理这个 ACC_SYNCHRONIZED 标志的,也许执行时内部会调整成一样。
顺道下来,第一个问题 不论是静态的或非静态的方法都加上 synchronized 关键字,那静态的方法和非静态的方法前加上 synchronized 关键字有区别吗?也有了答案,即非静态同步方法,监视区与 this 相关联,静态同步方法,监视区与该类的 Class 实例相关联。
立此题之前,本只想就 synchronized() 中的对象来个充分理解而已,无奈,事物间总是有千丝万缕,于是牵扯出这许多事。以后有空或有必要还是该拆出多个专题不垒这样的长篇大落,至少分出以下几出:
1. 不同线程执行同步方法或同步块的互斥规则
2. 同步时,监视区域是与哪一个对象相关联的
2. 如何理解同步块 synchronized(xxx) 中的对象参数
3. 同步块与同步方法的字节码分析
..... 或者还可以拟个 写同步方法时容易碰到的几个陷阱 等等
参考:1. The Java Virtual Machine Specification
2. Inside the Java Virtual Machine (by Bill Venners)
3. Books Related to the JVM
分享到:
相关推荐
最后,附带的教学PPT可能会包含线程创建、同步机制、并发工具类的实例分析,以及解决多线程问题的策略等内容,对深入理解和掌握Java多线程编程非常有帮助。通过实践这些实例,读者可以更好地运用多线程技术,提升...
在Java编程中,多线程同步是一个关键的概念,用于解决并发执行中的数据一致性问题。线程同步确保了多个线程在访问共享资源时按照预定的顺序或规则进行,防止数据竞争和死锁等异常情况发生。本文将深入探讨线程同步的...
学习多线程编程,你需要掌握如何创建和管理线程,如何解决同步和通信问题,以及如何避免和处理可能出现的异常情况。VC++提供了丰富的调试工具,帮助开发者定位和解决多线程中的问题。 在实际应用中,多线程常用于...
### C#中多线程与界面卡顿问题详解 #### 一、问题概述 在C#应用程序开发中,特别是Windows Forms应用中,界面卡顿是一个常见的问题。这往往发生在使用了多线程的情况下,尽管多线程技术可以有效提高程序性能,但...
《多线程小游戏详解》 在编程领域,游戏开发是一项技术含量高且充满挑战的工作,尤其是在实现复杂的交互和流畅的用户体验时。本文将探讨一款名为“多线程小游戏”的项目,该程序通过利用多线程技术来提升游戏性能,...
- **线程安全问题:** 如何避免共享资源访问冲突,确保多线程环境下的数据一致性。 - **核心API:** - **synchronized关键字:** 实现对象或代码块级别的独占锁,用于保证线程安全。 - **Lock接口:** 更灵活的...
#### 线程同步方法详解 在多线程编程中,线程同步是确保多个线程之间正确、安全地共享数据的关键技术。以下是一些常用的线程同步方法: 1. **wait()**:使一个线程进入等待状态并释放对象的锁。当调用此方法时,...
│ 高并发编程第二阶段38讲、多线程Active Objects设计模式(接受异步消息的主动对象)-上.mp4 │ 高并发编程第二阶段39讲、多线程Active Objects设计模式(接受异步消息的主动对象)-中.mp4 │ 高并发编程第二阶段40...
### 实验二、嵌入式Linux多线程编程实验知识点详解 #### 一、实验目的与基本要求概览 本次实验旨在深入理解嵌入式Linux环境下的多线程编程技术,包括线程的基本概念、创建与管理方法,以及通过信号量实现线程间的...
- **线程同步**:通过synchronized关键字、Lock接口(如ReentrantLock)等机制防止数据竞争,保证同一时刻只有一个线程访问共享资源。 - **线程通信**:使用wait()、notify()和notifyAll()方法,或者条件变量...
Java多线程和并发知识是Java开发中的重要组成部分,它涉及到如何高效地利用系统资源,尤其是在多核CPU环境下,合理地使用多线程可以显著提升应用程序的性能。 **1. 理论基础** 1.1 为什么需要多线程 多线程的引入...
在多线程或多处理器环境中,多个执行单元可能同时访问共享资源,这就需要有效的同步机制来避免数据竞争和不一致性问题。 《Linux内核同步操作详解》是一篇由Paul Rusty Russell撰写的关于Linux内核锁机制的详细指南...
Java并发编程是Java开发中的重要领域,涉及到多线程、并发控制、同步机制等多个知识点。以下是对Java并发知识体系的详细解析。 首先,我们要理解并发的理论基础。并发是为了提高系统资源利用率和处理能力,让多个...
### 嵌入式多线程实验报告知识点详解 #### 实验背景与目的 本次实验旨在通过实际操作加深对嵌入式系统中多线程编程的理解。实验的主要目的是让参与者熟悉Linux环境下多线程程序的设计与调试过程。具体而言,需要...
《Delphi多线程生命模拟程序详解》 生命模拟程序是一种计算机模拟实验,它尝试通过简单的规则来模拟生物系统的复杂行为。在编程领域,利用 Delphi 这样的高级编程语言来实现多线程的生命模拟程序,可以极大地提高...
《Linux多线程服务端编程:使用muduo C++网络库》主要讲述采用现代C++在x86-64 Linux上编写多线程TCP网络服务程序的主流常规技术,重点讲解一种适应性较强的多线程服务器的编程模型,即one loop per thread。...
《C#实现的坦克大战:多线程编程详解》 在计算机编程的世界中,游戏开发是一种极具挑战性和趣味性的实践。本文将深入探讨一个由C#语言编写的坦克大战项目,其中涉及到了多线程编程的关键概念和技术。通过分析这个...