[高级主题:关于synchronized]
其实在多线程编程基础部分,我已经谈过synchronized相关的内容.但临界区模式是其它多线程编程模式的基
础,所以在这里继续深入一下谈谈synchronized相关的一些内容.
只要见到synchronized关键字,第一要想到的问题就是,synchronized在保护谁?
在上面的例子中,synchronized保护的是Corrie对象的counter,name,number三个字段不被"交叉赋值",
也就是这三个字段同一时间只能被一个线程访问.
其次我们要考虑的问题是:这些对象都被妥善地保护了吗?
这是非常重要的问题.无论你花巨资打造一把高安全性锁,把自己的家门牢牢地锁住,可是你却把门旁边的窗子
敞开着,那么你花巨资打造的锁又要什么意义呢?所以要确保从任何一个通道访问被保护的对象都被加锁控制
的,比如字段是否都private或protected的,对于protected的子类中的扩展方法是否能保护被保护对象.
对于上面的例子因为display有可能被外面的方法单独调用,所以它也必须是同步的.而test方法只会在into中
调用,简单说它只是所有通道被加了锁的大房子中的一个小单元,所以不必担心有人会从外部访问它.
要注意保护的范围是三个同时需要保护的字段,如果它们被分别放在synchronized方法中保护,并不能保证它们
本个字段同时只有一个线程访问.
那么我们就有一个问题,获取谁的锁呢?
要保护一个对象,当然直接获取这个对象的锁,我们上面的例子可以理解为要同时保护三个对象,那么其实就是
要保护这三个对象的所在的容器.也就是它们所在的实例.如果不相关的三个对象要同时保护,一定要放在同时容纳
它们的容器中,否则无法同时保护它们的状态.对于上面的例子我们同样可以理解为要保护的是Corrie的实例,
因为这个实例是这三个字段的容器.所以我们用synchronized方法就是等同于synchronized(this){.......}
如果这个游戏中有多个山洞,而只有一块显示牌,那以我们就需要保护多个实例的三个字段同时只被一个线程
访问,我们就需要synchronized(Corrie.class)来保证多个实例被多个线程访问时只有一个对程能同时对三个
字段访问.
所以获取谁的锁定也是一个很重要的问题,如果你选择了错误的对象,就象你花巨资打了一把锁却锁了别人的
门.
synchronized就是原子操作,简单说在一个线程进行同步块中的代码时其它线程不能进入,这是很明显的.但同时,
多个同步方法或多个获取同一对象的同步块在同一时候也只能一个线程能访问其中之一,因为控制谁能访问的是要
获得那个同步对象的锁.如:
class C{
synchronized void a(){}
synchronized void b(){}
}
当一个线程进入同步方法a后那么其它线程当然不能进入a,同时也不能进入b,因为能进入的条件是获取this对
象的锁.一个结程进入a后this对象的锁被这个线程获取,其它线程进入b也同样要获取这个锁,而不仅仅是进入
a要获取这个锁.这一点一定要理解.
如果我们想所有线程只能有一个线程在同一时间访问a(),而这时也可以只有另一个线程可以访问b();就是a()和
b()身本都是同步的,但a()和b()之间不产生互斥相,那么就要为a()和b()分别创建用户于同步的对象,这样的
对象我们习惯叫虚拟锁。
class C{
Object aL = new Object();
Object bL = new Object();
void a(){
synchronized(aL){......}}
void b(){
synchronized(bL){......}
}
}
理解上面的知识我们再回过头来看原子操作.
JLS规定对于基本类型(除long和double)以外的赋值和引用都是原子操作,并且对于引用类型的赋值和引用也是
原子操作.
注意这里有两个方面的知识点:
1.对于long和double的操作非原子性的.需要说明这只是JLS的规定,但大多数JVM的实现其实已经保证了long和
double的赋值和引用也是原子性的,只是允许某种实现可以不是原子性的操作.
对于其它基本类型如int,如果一个线程执行x = 1;另一个线程执行x = 2;
由于可见性的问题(多线程编程系统中已经介绍),x要么就是1,要么就是2,看谁先同步到主存储区.
但对于long,l = 1;l = 2;分别由两个线程执行的结果有可能不是你想象的,它们有可能是0,或1,或2,或一个其
它的随机数,简单说两上线程中l的值的部分bit位可能被另一个线程改写.所以最可靠的是放在synchronized中
或用volatile 保护.当然这里说的是有“非常可靠”的需要。
2.我们看到,对于引用对象的赋值和引用也是原子的.
我们还是看javaworld上dcl的例子.
那个错误的例子误了好多人,(JAVA与模式的作者就是受害人),我们先不说JAVA内存模型的原因(前面我已经从
JAVA内存模型上说明了那个例子是错误的,我是说对那个例子的分析是错误的).单从对于"引用对象的赋值和引
用也是原子的"这句话,就知道对于引用字段的赋值,绝对不可能出现先分配空间,然后再还没有被始化或还没有
调构造方法之前又被别的线程引用.因为当一个线程在执行赋值的时候是原子性的操作,其它线程的引用操作也是
原子性的操作的,在赋值操作没有完成之前其它线程根本不可能见到"分配了空间却没有初始化或没有调用构造方法"
的这个对象.
不知道什么原因,这样的一个例子从它诞生开始竟然是所有人都相信了,也许有人责疑过但我不知道.如果你有足
够的基础知识,就不必跟着别人的感觉走!
因为这是一个最最基础的模式,暂时不介绍它与其它模式的关系.在以后介绍其它模式时反过来再和它进行比较.
而一些复杂的模式都是在这个简单的模式的基础上延伸的.
分享到:
相关推荐
《Java多线程编程实战指南》这本书深入浅出地讲解了Java多线程的核心概念和实战技巧,分为核心篇和设计模式篇,旨在帮助开发者掌握并应用多线程技术。 1. **线程基础** - **线程的创建**:Java提供了两种创建线程...
- **第一章**:关于多线程编程,介绍了多线程的基本概念、术语以及设计技巧。 - **第二章**:线程管理,讨论了如何创建线程、配置线程属性及编写线程主体。 - **第三章**:RunLoops,深入探讨了RunLoops的工作原理...
在Linux系统中,多线程编程是一种常见的编程模式,它允许多个执行流在同一进程中并发运行,从而提高程序的效率和响应性。本篇将深入介绍Linux多线程编程的基本概念、实现方法以及注意事项。 首先,多线程是通过创建...
在深入探讨Java多线程设计模式的知识点之前,让我们首先明确多线程编程在Java中的地位和作用。Java自诞生之日起就内置了对多线程编程的支持,这使得开发能够充分利用多核处理器性能的应用程序成为可能。多线程设计...
在多线程编程中,有一些常见的设计模式可以帮助开发者更好地组织代码,例如生产者-消费者模型、读写锁、工作窃取等。理解这些模式有助于编写出高效且可靠的多线程程序。 ### 7. 性能优化 多线程编程的一个主要目标...
### ASP.NET多线程编程(一) #### 课程概述 本课程由讲师邵志东讲解,旨在介绍ASP.NET环境下的多线程编程技术。课程针对有一定基础的学习者(Level200),要求学习者事先安装好.NET Framework以及VS.NET 2002/2003...
综上所述,多线程编程技术是现代软件开发中的核心技能之一,它不仅提升了程序的执行效率,也使得程序设计更加灵活和模块化。理解和掌握多线程编程,对于开发高效、健壮的应用至关重要。通过深入学习和实践,开发者...
最后,多线程编程的效率和稳定性很大程度上取决于程序员对并发编程模式和线程安全机制的理解和应用。一个良好的多线程程序应该能够充分利用多核处理器的优势,同时确保数据的一致性和程序的稳定性。 因此,Win32多...
### 多线程编程指南(苹果公司多线程编程指南) #### 重要概念与知识点 **一、关于多线程编程** ##### 1.1 什么是多线程 多线程是指在一个程序或进程中能够同时运行多个线程,每个线程执行不同的任务。通过这种...
9. **死锁和活锁**:在多线程编程中,必须注意潜在的死锁(两个或更多线程互相等待对方释放资源)和活锁(线程因不断重试而导致无法继续执行)问题,通过合理的资源管理和同步策略来避免。 10. **性能监控**:在...
【MFC弹球游戏(多线程编程)】 在计算机编程领域,MFC(Microsoft Foundation Classes)是微软提供的一套面向对象的C++类库,它使得开发者能够更方便地利用Windows API进行Windows应用程序开发。MFC弹球游戏是利用...
《Windows多线程编程技术与实例》是一本深入探讨Windows平台下多线程编程的书籍,作者郝文化通过丰富的实例和源代码,为读者揭示了多线程编程的核心概念、设计模式以及实战技巧。这本书旨在帮助开发者提升在Windows...
在多线程编程领域,理解和掌握其基本概念至关重要。线程是操作系统调度的基本单位,一个进程中可以包含多个线程,它们共享同一内存空间,能够并发执行任务,提高系统资源利用率和程序响应速度。书中详细阐述了创建、...
### C++多线程编程入门知识点详解 #### 进程与线程概念及背景 - **进程**: 进程是程序的一次动态执行过程,每个进程都有独立的内存空间和其他资源,是操作系统进行资源分配的基本单位。 - **线程**: 线程是进程中...