synchronized(同步锁)关键字是用来解决共享资源的竞争问题。基本上所有的并发模式在解决线程冲突问题时,都是采用序列化访问共享资源的方案。这意味着在任意时刻,只允许一个任务访问共享资源。通常这是通过在代码前加上一条锁语句来实现的,这就使得在给定时刻,只允许一个任务可以运行这段代码。因为锁语句产生了一种互相排斥的效果,这种机制常常被称作互斥量。
要控制对共享资源的访问,得先把它装进一个对象。然后把所有想访问这个资源的方法都标记为synchronized。如果某个任务处于对一个标记为synchronized的方法的调用中,那么在这个线程从该方法返回之前,其他所有要调用类中被标记为synchronized的方法都将处于阻塞状态。
synchronized void f(){
}
synchronized void g(){
}
所有对象都自动含有单一的锁(也称为监视器)。
在使用并发时,将域设置为private是非常重要的,否则,synchronized关键字就不能防止其他任务直接访问域,这将产生冲突。
一个任务可以多次获得对象的锁,这表现为在标记为synchronized的一个方法中调用另一个标记为synchronized的方法,甚至可以调用多层。JVM将对调用层数进行计数,直至锁被完全释放计数才被置0。
针对每个类,也有一个锁(作为类Class对象的一部分),所以synchronized static方法可以在类的范围内防止对static数据的并发访问。
每个访问临界共享资源的方法都必须被同步,否则它们就不能正确的工作。
public abstract class IntGenerator {
private volatile boolean canceled=false;
public abstract int next();
public void cancel() {
canceled=true;}
public boolean isCanceled() {
return canceled;}
}
public class EvenChecker implements Runnable{
private IntGenerator generator;
private final int id;
public EvenChecker(IntGenerator g,int ident) {
generator=g;
id=ident;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(!generator.isCanceled()) {
int val=generator.next();
if(val%2!=0) {
System.out.println(val+" is not even");
}
generator.cancel();
}
}
public static void test(IntGenerator gp,int count) {
System.out.println("Press Control-c to exit");
ExecutorService exec=Executors.newCachedThreadPool();
for(int i=0;i<count;i++) {
exec.execute(new EvenChecker(gp,i));
}
exec.shutdown();
}
public static void test(IntGenerator gp) {
test(gp,10);
}
}
public class EvenGenerator extends IntGenerator{
private int currentEvenValue=0;
public synchronized int next() {
++currentEvenValue;
Thread.yield();
++currentEvenValue;
return currentEvenValue;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
EvenChecker.test(new EvenGenerator());
}
}
若去掉EvenGenertor中修饰next()方法的synchronized关键字和让步语句,将导致多个线程执行混乱。即一个线程并不会执行两次自加操作。