Java同步块(synchronized block)使用详解

Java 同步块(synchronized block)用来标记方法或者代码块是同步的。Java同步块用来避免竞争。本文介绍如下内容:java

  • Java同步关键字(synchronzied)ide

  • 实例方法同步this

  • 静态方法同步spa

  • 实例方法中同步块线程

  • 静态方法中同步块orm

  • Java同步示例对象

Java 同步关键字(synchronized)

Java中的同步块用synchronized标记。同步块在Java中是同步在某个对象上。全部同步在一个对象上的同步块在同时只能被一个线程进入并执行操做。全部其余等待进入该同步块的线程将被阻塞,直到执行该同步块中的线程退出。同步

有四种不一样的同步块:虚拟机

  1. 实例方法it

  2. 静态方法

  3. 实例方法中的同步块

  4. 静态方法中的同步块

上述同步块都同步在不一样对象上。实际须要那种同步块视具体状况而定。

实例方法同步

下面是一个同步的实例方法:

public synchronized void add(int value){this.count += value;
 }

注意在方法声明中同步(synchronized )关键字。这告诉Java该方法是同步的。

Java实例方法同步是同步在拥有该方法的对象上。这样,每一个实例其方法同步都同步在不一样的对象上,即该方法所属的实例。只有一个线程可以在实例方法同步块中运行。若是有多个实例存在,那么一个线程一次能够在一个实例同步块中执行操做。一个实例一个线程。

静态方法同步

静态方法同步和实例方法同步方法同样,也使用synchronized 关键字。Java静态方法同步以下示例:

public static synchronized void add(int value){
 count += value;
 }

一样,这里synchronized 关键字告诉Java这个方法是同步的。

静态方法的同步是指同步在该方法所在的类对象上。由于在Java虚拟机中一个类只能对应一个类对象,因此同时只容许一个线程执行同一个类中的静态同步方法。

对于不一样类中的静态同步方法,一个线程能够执行每一个类中的静态同步方法而无需等待。无论类中的那个静态同步方法被调用,一个类只能由一个线程同时执行。

实例方法中的同步块

有时你不须要同步整个方法,而是同步方法中的一部分。Java能够对方法的一部分进行同步。

在非同步的Java方法中的同步块的例子以下所示:

public void add(int value){

    synchronized(this){       this.count += value;
    }
  }

示例使用Java同步块构造器来标记一块代码是同步的。该代码在执行时和同步方法同样。

注意Java同步块构造器用括号将对象括起来。在上例中,使用了“this”,即为调用add方法的实例自己。在同步构造器中用括号括起来的对象叫作监视器对象。上述代码使用监视器对象同步,同步实例方法使用调用方法自己的实例做为监视器对象。

一次只有一个线程可以在同步于同一个监视器对象的Java方法内执行。

下面两个例子都同步他们所调用的实例对象上,所以他们在同步的执行效果上是等效的。

public class MyClass {   public synchronized void log1(String msg1, String msg2){      log.writeln(msg1);      log.writeln(msg2);
   }   public void log2(String msg1, String msg2){
      synchronized(this){         log.writeln(msg1);         log.writeln(msg2);
      }
   }
 }

在上例中,每次只有一个线程可以在两个同步块中任意一个方法内执行。

若是第二个同步块不是同步在this实例对象上,那么两个方法能够被线程同时执行。

静态方法中的同步块

和上面相似,下面是两个静态方法同步的例子。这些方法同步在该方法所属的类对象上。

public class MyClass {    public static synchronized void log1(String msg1, String msg2){       log.writeln(msg1);       log.writeln(msg2);
    }    public static void log2(String msg1, String msg2){
       synchronized(MyClass.class){          log.writeln(msg1);          log.writeln(msg2);
       }
    }
  }

这两个方法不容许同时被线程访问。

若是第二个同步块不是同步在MyClass.class这个对象上。那么这两个方法能够同时被线程访问。

Java同步实例

在下面例子中,启动了两个线程,都调用Counter类同一个实例的add方法。由于同步在该方法所属的实例上,因此同时只能有一个线程访问该方法。

public class Counter{     long count = 0;     public synchronized void add(long value){       this.count += value;
     }
  }  public class CounterThread extends Thread{     protected Counter counter = null;     public CounterThread(Counter counter){        this.counter = counter;
     }     public void run() {    for(int i=0; i<10; i++){
           counter.add(i);
        }
     }
  }  public class Example {    public static void main(String[] args){
      Counter counter = new Counter();
      Thread  threadA = new CounterThread(counter);
      Thread  threadB = new CounterThread(counter);

      threadA.start();
      threadB.start();
    }
  }

建立了两个线程。他们的构造器引用同一个Counter实例。Counter.add方法是同步在实例上,是由于add方法是实例方法而且被标记上synchronized关键字。所以每次只容许一个线程调用该方法。另一个线程必需要等到第一个线程退出add()方法时,才能继续执行方法。

若是两个线程引用了两个不一样的Counter实例,那么他们能够同时调用add()方法。这些方法调用了不一样的对象,所以这些方法也就同步在不一样的对象上。这些方法调用将不会被阻塞。以下面这个例子所示:

public class Example {   public static void main(String[] args){
     Counter counterA = new Counter();
     Counter counterB = new Counter();
     Thread  threadA = new CounterThread(counterA);
     Thread  threadB = new CounterThread(counterB);

     threadA.start();
     threadB.start();
   }
 }

注意这两个线程,threadA和threadB,再也不引用同一个counter实例。CounterA和counterB的add方法同步在他们所属的对象上。调用counterA的add方法将不会阻塞调用counterB的add方法。

相关文章
相关标签/搜索