一、概述

Java 同步块(synchronized block)用来标记方法或者代码块是同步的。Java同步块用来避免竞争。

Java 同步关键字(synchronized)

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

二、类型

有四种不同的同步块:

  1. 实例方法
  2. 静态方法
  3. 实例方法中的同步块
  4. 静态方法中的同步块

上述同步块都同步在不同对象上。实际需要那种同步块视具体情况而定。

三、详解

1、实例方法同步

示例:

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

说明:

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

2、静态方法同步

示例:

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

说明:

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

3、实例方法中的同步块

不需要同步整个方法,而是同步方法中的一部分。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实例对象上,那么两个方法可以被线程同时执行。

4、静态方法中的同步块

示例:下面是两个静态方法同步的例子。这些方法同步在该方法所属的类对象上。这两个方法不允许同时被线程访问。

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);
       }
    }
}

完整示例:

public class Counter {

	private int count = 0;
	
	public synchronized void add(int value){
		count += value;
		System.out.println(Thread.currentThread().getName() + "-value:"+ value + "-> Count:" + count);
	}
	
	public int getCount(){
		return count;
	}
}

public class CounterThread extends Thread{

	private Counter counter;
	
	public CounterThread(Counter counter){
		this.counter = counter;
	}
	
	public void run() {
	    for(int i=0;i<=5;i++){
	    	counter.add(i);
	    }
	}
}

public class CounterTest {

	public static void main(String[] args) {
		Counter counter = new Counter();
		CounterThread thread1 = new CounterThread(counter);
		CounterThread thread2 = new CounterThread(counter);
		
		thread1.start();
		thread2.start();
		
		/** console结果:
		 *  Thread-0-value:0-> Count:0
			Thread-0-value:1-> Count:1
			Thread-0-value:2-> Count:3
			Thread-0-value:3-> Count:6
			Thread-0-value:4-> Count:10
			Thread-0-value:5-> Count:15
			Thread-1-value:0-> Count:15
			Thread-1-value:1-> Count:16
			Thread-1-value:2-> Count:18
			Thread-1-value:3-> Count:21
			Thread-1-value:4-> Count:25
			Thread-1-value:5-> Count:30
		 */
	}
}