`

[#0x004C] Java多线程:synchronized

    博客分类:
  • Java
阅读更多

  首先明确一点,同步方法本质上也是一个同步控制块(仅针对于锁定this的情况,如果同步控制块锁定的不是this,那么它是不能直接改写为同步方法的),区别在于同步方法的粒度是整个方法,而同步控制块的粒度可以是方法的一部分。

// 同步方法示例
public class Counter {
	int count;
	static int classCount;
	
	public synchronized void bump() {
		System.out.println("bump() starts");
		count++;
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("bump() ends");
	}
	
	public static synchronized void classBump() {
		classCount++;
		System.out.println("classBump()");
	}
}

// 同步方法可以等价地写成同步控制块
public class Counter {
	int count;
	static int classCount;
	
	public void bump() {
		synchronized (this) {
			// ...
		}
	}
	
	public static void classBump() {
		synchronized (Counter.class) {
			// ...
		}
		
		// 或者不用Counter.class,用Class.forName()
		/**
		try {
			synchronized (Class.forName("Counter")) {
				// ...
			}
		} catch (ClassNotFoundException e) {
			e.printStack();
		}
		*/
	}
}

 

  从这个例子可以看出,Class锁(针对static方法)和Object锁(针对非static方法)是两种锁。从实验的结果来看,这两种锁是不冲突的,即:假设有Counter c = new Counter();,如果Thread A调用c.bump()方法,那么synchronized(this)只是锁定了c这个对象,Thread B可以无所顾忌地调用Counter.classBump()(或者c.classBump()也可以),不用等待Thread A释放c的锁。如:

public class MultiThreadTest8 {
	public static void main(String[] args) {
		final Counter c = new Counter();
		
		Thread t1 = new Thread(
				new Runnable() {
					@Override public void run() {
						c.bump();
					}
				}, "Runner 1");
		
		Thread t2 = new Thread(
				new Runnable() {
					@Override public void run() {
						//Counter.classBump();
						c.classBump();
					}
				}, "Runner 2");
		
		t1.setPriority(Thread.NORM_PRIORITY + 2);
		t1.start();
		t2.start();
		
		// output: 
		/**
			bump() starts
			classBump()
			bump() ends		
		*/
	}
}

 

  不过一旦Thread A调用c.bump(),锁定了c,那么Thread B就不能调用c.bump()了,c中的其他同步方法或是同步控制块Thread B也不能访问,只有等到Thread A释放c的锁(即c.bump()同步部分执行完)。Counter的非同步方法不受锁的限制,即使Thread A锁定了c,Thread B也可以随意访问c的非同步方法。

 

p.s. 这里说锁定一个对象,并不是说属性不能访问,锁定对象只是锁定同步方法或是同步控制块,使同步方法或是同步控制块的执行不会被其他线程打断(可以理解为:锁定方法到一个线程)。如果有一个同步方法去修改某个字段,此时是可以有另一个非同步方法也去修改这个字段,这样仍然有可能产生数据不一致的情况。

 

p.s. synchronized关键字可以放在类的前面,表示类中的所有方法都是同步方法。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics