每个锁关联一个请求计数和一个占有它的线程.当计数为0时,表示该锁未被占有.此时线程请求时,JVM将记录锁的占有线程,并将请求计数加1.如果同一线
程再次请求这个锁,计数再加1.每次退出synchronized 标识的块时计数会减1.当计数为0时,锁被释放.
会影响性能.所以我们最好是将一些需要同步的原子操作放在同步块中.如下面这种做法:
synchronized (this) {
++hits;
if (i.equals(lastNumber)) {
++cacheHits;
factors = lastFactors.clone();
}
}
if (factors == null) {
factors = factor(i);
synchronized (this) {
lastNumber = i;
lastFactors = factors.clone();
}
}
块中都只有很简短的代码.第一个块保护着检查再运行的操作以检查对我们很重要的状态码,另一个进行数据的更新.
private static boolean ready;
private static int number;
private static class ReaderThread extends Thread {
public void run() {
while (!ready)
Thread.yield();
System.out.println(number);
}
}
public static void main(String[] args) {
new ReaderThread().start();
number = 42;
ready = true;
}
上面的mian主线程运行时还充当了"写线程",并且新建"读线程"并让它运行.读线程会不断的循环直到ready的值为true.但在有些情况下,上面的程序会和我们想象的输入42相异:
由于JAVA的"重排序"机制(JVM:只要代码顺序改变对结果不产生影响,那么就不能保证代码执行的顺序是书写的顺序)可能在对number设置值前ready的值就已经是true了.那么输入的结果会是0.
后可以解决这一问题.
等64位的数据则被做为两个32位数进行运算.
test;
类型后,编译器与运行时会监视这个变量,而且对它的操作不会与其他的内在操作重排序.它不会缓存在寄存器或者缓存在其它地方,所以读一个volatile
类型的变量时,它总是返回由某一线程所写入的最新值.我们可以将它看做轻量级的同步机制.
private int value;
public synchronized int get() {
return value;
}
public synchronized void set(int value) {
this.value = value;
}
如上代码可以被:volatile private int
value;以及不加同步声明的getter,setter方法所代替.但当然会牺少许功能:加锁可以保证可见性和原子性,但volatile变量只能保证可见性.所以,在不需要原子性的时候,可以用它.