Java 彻底弄明白synchronized的使用

多个线程访问共享资源(临界资源)的时候,会出现线程安全问题,安全问题大多数是可见性和原子性问题。但这样说可能并不严谨,线程的安全性可能更在于他对错误性的定义,当多个线程访问一个类时,如果可以需要考虑运行时环境的调度和交换,并且需要额外的同步保证结果正确,我们认为这个线程是有线程安全性问题的。下面我们讨论一下可见性和原子性带来的线程安全问题。

可见性的问题

例如执行多个线程执行a++,那么多个线程就会被分配到不同的处理器上,每个处理器都从主存上复制操作一份拷贝,处理完成后复制给主存。由于分配到了不同的处理器上,两个线程的操作可能会互相覆盖,这样的结果就会和预想的又偏差。例如如下代码:

[html] view plain copy
  1. private volatile static int a=0;  
  2.     private static void add(){  
  3.         a++;  
  4.     }  
  5.       
  6.     public static void main(String[] args) throws InterruptedException {  
  7.         // TODO Auto-generated method stub  
  8.         for(int i=0;i<1000;i++){  
  9.             new Thread(new Runnable() {  
  10.                   
  11.                 @Override  
  12.                 public void run() {  
  13.                     add();  
  14.                 }  
  15.             }).start();  
  16.         }  
  17.         System.out.println(a);  
  18.               
  19.     }  

上面的例子中,我们期望是1000,然而他却有时候输出997,998的结果,这是因为线程A执行这个操作之后,并没有返回过去,导致线程B访问的时候,访问到的不是最新的数据。当然上面也有原子性的问题,不过分开讨论。

原子性分解的问题

原子性就是不可分割的操作,在硬件层面上是指不被线程调度器中断,也就是说一个操作要么执行完,要么不执行。上面的例子中a++就不是原子性操作,首先要读取X的值,然后+1,最后写入工作内存中。三个步骤任何一部打断,都会影响最终的结果。但是a=1和return就是原子性操作了。

synchronized关键字

为了解决上述的原子性和可见性带来的线程安全问题,Java提供了同步机制互斥锁机制,这个机制保证了在同一时间内只有一个线程能访问共享资源(临界资源)。这个机制的保障来源于监视锁Monitor,在Java中,每个对象都自带监视锁,当我们要访问用synchronized修饰的方法或代码块的时候,都意味着进入这个方法或者代码块要加锁,离开要放锁。而且Synchronizd可以显示的说明对哪个对象加锁,如下例子:
[html] view plain copy
  1. synchronized public void add(){  
  2.     a++;  
  3. }  
  4. // 等价于  
  5. public void add(){  
  6.     synchronized(this){  
  7.         a++;  
  8.     }  
  9. }  

对同步机制互斥锁小结

1、每个对象有自己的监视锁Monitor,这意味着多个线程访问一个对象的Synchronizd方法或者代码块时,需要等其他线程放锁才可访问。相对的多个对象的监视锁不存在互斥情况。
2、互斥机制只能保证Synchronizd代码块里的代码同步,而不再代码块里的代码不能得到保证。
3、子类重写父类的Synchronizd方法不能保证同步。因为Synchronizd并不属于方法定义的一部分。
4、SYnchronizd修饰静态方法时,等同于给该类的所有对象都加了一把锁。因为静态方法属于类,不属于对象。
5,Synchronizd把类当监视锁的话,效果等同4.
5、Synchronizd修饰的方法的监视锁(Monitor)也是重入锁,也就是说当一个线程获得监视锁之后,可以再次获得该对象的锁。例如,线程A调用一个对象的同步方法之后,可以调用该对象的另一个同步方法。




参考资料:

Java并发开发实战

兰亭风雨:http://blog.csdn.net/ns_code/article/details/17199201

阳光日志:http://blog.csdn.net/luoweifu/article/details/46613015

海子:http://www.cnblogs.com/dolphin0520/p/3923737.html


猜你喜欢

转载自blog.csdn.net/liyongshun82/article/details/79565138
今日推荐