java的关键字synchronized为防止资源冲突提供了内置支持。当任务要执行被synchronized关键字保护起来的代码段时,它将检查锁是否可用,然后获取锁、执行代码、释放锁。
1.锁的粒度
1.1作用于普通方法
锁的粒度是当前对象。
在对象上调用其任意synchronized方法时,此对象(this)被加锁,此时该对象上的其他synchronized方法只有等到前一个方法调用完毕并释放了锁之后才能被调用。
两个对象并发调用同一synchronized方法,并不会互斥。
1.2所用于静态方法
锁的是这个类,即XX.class
1.3 锁住指定对象
如 synchronized (obj) {...}。
2.代码示例
我们期望generator.next()每次都产生的是偶数,这在单线程下没任何问题。但多线程下就会有产生奇数的可能。
import java.util.concurrent.*;
public class EvenGenerator {
public boolean isCanceled = false;
private volatile int currentEvenValue = 0;
public int next() {
++currentEvenValue; // Danger point here!
++currentEvenValue;
return currentEvenValue;
}
public static void main(String[] args) throws InterruptedException {
ExecutorService exec = Executors.newCachedThreadPool();
EvenGenerator g=new EvenGenerator();
for (int i = 0; i < 5; i++)
exec.execute(new EvenChecker(g ));
exec.shutdown();
TimeUnit.SECONDS.sleep(5);
if(!g.isCanceled){
exec.shutdownNow();
System.out.println("5秒足够长,若能执行到这里说明不会产生奇数");
}
}
}
class EvenChecker implements Runnable {
private EvenGenerator generator;
public EvenChecker(EvenGenerator generator){
this.generator=generator;
}
public void run() {
while (!generator.isCanceled) {
int val = generator.next();
if (val % 2 != 0) {
System.out.println(val + " not even!");
generator.isCanceled=true; // Cancels all EvenCheckers
}
}
}
}
/*
5359 not even!
5363 not even!
5361 not even!
5357 not even!
*/
将 public int next() {} 改为synchronized public int next() {} 即可解决问题。所有对象都自动含有单一的锁(也成为监视器)。
Thread.yield()告诉java线程调度器“重要的部分我已经完成,此刻可以将我挂起,切换给其他线程执行”。这个函数可用可不用,但用了它更容易按照自己的掌控去切换线程,从而验证一些东西。