快精灵印艺坊 您身边的文印专家
广州名片 深圳名片 会员卡 贵宾卡 印刷 设计教程
产品展示 在线订购 会员中心 产品模板 设计指南 在线编辑
 首页 名片设计   CorelDRAW   Illustrator   AuotoCAD   Painter   其他软件   Photoshop   Fireworks   Flash  

 » 彩色名片
 » PVC卡
 » 彩色磁性卡
 » 彩页/画册
 » 个性印务
 » 彩色不干胶
 » 明信片
   » 明信片
   » 彩色书签
   » 门挂
 » 其他产品与服务
   » 创业锦囊
   » 办公用品
     » 信封、信纸
     » 便签纸、斜面纸砖
     » 无碳复印纸
   » 海报
   » 大篇幅印刷
     » KT板
     » 海报
     » 横幅

Java同步机制浅谈――synchronized对代码作何影响?

Java对多线程的支持与同步机制深受大家的喜爱,好像看起来使用了synchronized要害字就可以轻松地解决多线程共享数据同步问题。到底如何??D?D还得对synchronized要害字的作用进行深入了解才可定论。
总的说来,synchronized要害字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方式和同步语句块。假如再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
在进一步阐述之前,我们需要明确几点:
A.无论synchronized要害字加在方式上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁?D?D而且同步方式很可能还会被其他线程的对象访问。
B.每个对象只有一个锁(lock)与之相关联。
C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

接着来讨论synchronized用到不同地方对代码产生的影响:

假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方式,P1、P2就都可以调用它们。

1.  把synchronized当作函数修饰符时,示例代码如下:
Public synchronized void methodAAA()
{
//….
}
这也就是同步方式,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方式对象。也就是说,当一个对象P1在不同的线程中执行这个同步方式时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加了synchronized要害字的方式。
上边的示例代码等同于如下代码:
public void methodAAA()
{
synchronized (this)      //  (1)
{
       //…..
}
}
 (1)处的this指的是什么呢?它指的就是调用这个方式的对象,如P1。可见同步方式实质是将synchronized作用于object reference。?D?D那个拿到了P1对象锁的线程,才可以调用P1的同步方式,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下挣脱同步机制的控制,造成数据混乱:(
2.同步块,示例代码如下:
            public void method3(SomeObject so)
              {
                     synchronized(so)
{
       //…..
}
}
这时,锁就是so这个对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特别的instance变量(它得是一个对象)来充当锁:
class Foo implements Runnable
{
       private byte[] lock = new byte[0];  // 特别的instance变量
    Public void methodA()
{
       synchronized(lock) { //… }
}
//…..
}
注:零长度的byte数组对象创建起来将比任何对象都经济?D?D查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
3.将synchronized作用于static 函数,示例代码如下:
      Class Foo
{
public synchronized static void methodAAA()   // 同步的static 函数
{
//….
}
public void methodBBB()
{
       synchronized(Foo.class)   //  class literal(类名称字面常量)
}
       }
   代码中的methodBBB()方式是把class literal作为锁的情况,它和同步的static函数产生的效果是相同的,取得的锁很特殊,是当前调用这个方式的对象所属的类(Class,而不再是由这个Class产生的某个详细对象了)。
记得在《Effective Java》一书中看到过将 Foo.class和 P1.getClass()用于作同步锁还不相同,不能用P1.getClass()来达到锁这个Class的目的。P1指的是由Foo类产生的对象。

可以推断:假如一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方式时,不会构成同步,因为它们的锁都不相同。A方式的锁是Obj这个对象,而B的锁是Obj所属的那个Class。

小结如下:
搞清晰synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程序。

 
还有一些技巧可以让我们对共享资源的同步访问更加安全:
1.  定义private 的instance变量+它的 get方式,而不要定义public/protected的instance变量。假如将变量定义为public,对象在外界可以绕过同步方式的控制而直接取得它,并改动它。这也是JavaBean的标准实现方法之一。
2.  假如instance变量是一个对象,如数组或ArrayList什么的,那上述方式仍旧不安全,因为当外界对象通过get方式拿到这个instance对象的引用后,又将其指向另一个对象,那么这个private变量也就变了,岂不是很危险。 这个时候就需要将get方式也加上synchronized同步,并且,只返回这个private对象的clone()?D?D这样,调用端得到的就是对象副本的引用了。




返回类别: 教程
上一教程: JAVA中文问题解决总结
下一教程: Java社区选举结果

您可以阅读与"Java同步机制浅谈――synchronized对代码作何影响?"相关的教程:
· 浅谈 Java 中 this 的使用
· 100行Java代码构建一个线程池
· 诊断 Java 代码:设计轻松的代码维护
· JAVA代码编写的30条建议 选择自 chinaewolf 的 Blog
· 浅谈Java Virtual Machine
    微笑服务 优质保证 索取样品