`
wuhongyu
  • 浏览: 404495 次
  • 性别: Icon_minigender_1
  • 来自: 哈尔滨
社区版块
存档分类
最新评论

JAVA 线程学习笔记--同步

阅读更多

为了确保可以在线程之间以受控方式共享数据,Java 语言提供了两个关键字:synchronized 和volatile。

 

Synchronized 有两个重要含义:它确保了一次只有一个线程可以执行代码的受保护部分(互斥,mutual exclusion 或者说 mutex),而且它确保了一个线程更改的数据对于其它线程是可见的(更改的可见性)。
Volatile 比同步更简单,只适合于控制对基本变量(整数、布尔变量等)的单个实例的访问。当一个变量被声明成 volatile,任何对该变量的写操作都会绕过高速缓存,直接写入主内存,而任何对该变量的读取也都绕过高速缓存,直接取自主内存。这表示所有线程在任何时候看到的 volatile 变量值都相同。

 

Synchronized :

    每个 Java 对象都有一个相关的锁。同一时间只能有一个线程持有 Java 锁。当线程进入 synchronized 代码块时,线程会阻塞并等待,直到锁可用,当它可用时,就会获得这个锁,然后执行代码块。当控制退出受保护的代码块时,即到达了代码块末尾或者抛出了没有在 synchronized 块中捕获的异常时,它就会释放该锁。
    需要注意的是,无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁。但即使一个 Java 对象的锁正被其他线程占用时,仍可访问这个对象中没有 synchronized 修饰的代码块。


假设多个线程共享一个计数器,如下:
public class Counter {
   private int counter = 0;
   public int get() { return counter; }
   public void set(int n) { counter = n; }
   public void increment() {
      set(get() + 1);
   }
}


    要使递增操作正确运行,不仅get() 和 set() 必须是 synchronized,而且 increment() 也必需是synchronized!否则,调用 increment() 的线程可能会中断另一个调用 increment() 的线程。如果不走运,最终结果将会是计数器只增加了一次,不是两次。同步 increment() 防止了这种情况的发生,因为整个递增操作是原子的。

 

synchronized作用于方法时,锁定的是调用这个同步方法对象。当一个对象P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加了synchronized关键字的方法。

 

public class TestSynchronized extends Thread{

    public synchronized void method(){
            System.out.println(this.currentThread().getName() + " - method() begin");
            try {
                    Thread.sleep(20000);
            } catch (InterruptedException e) {
                    e.printStackTrace();
            }
            System.out.println(this.currentThread().getName() + " - method() over");
    }

    public void run() {
            method();
    }
   
    public static void main(String[] args) {
        /*
         * 同一个对象:
         * 当同一个对象在不同的线程中,执行一个同步方法时,它们之间会形成互斥,达到同步的效果
         */
        TestSynchronized ts = new TestSynchronized();
        Thread t1 = new Thread(ts);
        Thread t2 = new Thread(ts);
        t1.start();
        t2.start();
       
           
        /*
         * 不同的对象:
         * 当同一个Class产生的不同对象在不同的线程中执行一个同步方法时,
         * 他们可以任意调用这个被加了synchronized关键字的方法,而互不影响,不形成互斥,达不到同步效果。
         * 也就是说此时synchronized并未起到任何作用,与不加这个关键字效果是一样的。
         *
        TestSynchronized ts1 = new TestSynchronized();
        TestSynchronized ts2 = new TestSynchronized();
        Thread t1 = new Thread(ts1);
        Thread t2 = new Thread(ts2);
        t1.start();
        t2.start();
        */
    }
}

同一个对象的运行结果:
Thread-1 - method() begin
Thread-1 - method() over
Thread-2 - method() begin
Thread-2 - method() over

不同的对象的运行结果:
Thread-1 - method() begin
Thread-2 - method() begin
Thread-1 - method() over
Thread-2 - method() over

 

其中,

public synchronized void method(){
        ......
 }
相当于
public synchronized void method(){
         synchronized (this){ //this指的就是调用这个方法的对象
            ......
         }
 }

 

synchronized作用于同步块时,示例代码如下:
public void method(SomeObject so)
{
  synchronized(so)
  {
   //…..
 }
}


  这时,锁就是so这个对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁:


  class Foo implements Runnable
  {
    private byte[] lock = new byte[0]; // 特殊的instance变量
    Public void method()
    {
      synchronized(lock) { //… }
    }
   //…..
  }
  注:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。

分享到:
评论
1 楼 dngoryaner 2011-09-15  
[color=red][/color][size=xx-large][/size][align=center][/align]

相关推荐

Global site tag (gtag.js) - Google Analytics