java中同步(synchronized)访问共享的可变数据及原子性操作
当多个线程共享可变数据的时候,每个读或者写数据的线程都必须执行同步。如果没有同步,就无法保证一个线程所做的修改可以被另外一个线程获知。未能同步共享可变数据会造成程序的活性失败(liveness failure)和安全性失败(safety failure)。这样的失败是最难以调试的。它们可能是间歇性的,切与时间相关,程序的行为在不同的VM上可能根本不同。如果只需要线程之间的交互通信,而不需要互斥,volatile修饰符就是一种可以接受的同步形式,但要正确地使用它可能需要一些技巧。
“原子操作(atomic operation)是不需要synchronized”,这是Java多线程编程的老生常谈了。所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切 [1] 换到另一个线程)。
1.1 关键字synchronized
关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法,或者某一个代码块。
如下示例:
package thread;
import java.util.concurrent.TimeUnit;
public class StopThreadSynch {
private static boolean stopRequested;
public static synchronized boolean isStopRequested() {
return stopRequested;
}
public static synchronized void setStopRequested(boolean stopRequested) {
StopThreadSynch.stopRequested = stopRequested;
}
public static void main(String[] args) throws InterruptedException {
long startDate = System.currentTimeMillis();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while(!isStopRequested()) {
i++;
System.out.println(i);
}
}
});
thread.start();
TimeUnit.SECONDS.sleep(1);
setStopRequested(true);
long endDate = System.currentTimeMillis();
System.out.println(endDate - startDate);
System.out.println("-----------");
}
}
注意写方法和读方法都是被同步了,如果只有写方法被同步而读方法没有被同步,同步就不会起作用。
1.2 看如下的例子
private static int nextNumber = 0;
public static synchronized int generateNumber() {
return nextNumber++;
}
这个方法的目的是要确保每个调用都返回不同的值(只要不超过2的32次方),所以在方法块上加了synchronized同步关键字,这样可以确保多个调用不会交叉存取,确保每个调用都会看到之前所有的调用的效果。
另外一种方法是使用java.util.concurrent.atomic的一部分,这个包下面有我们常用的几个类:
java.util.concurrent.atomic.AtomicBoolean,
java.util.concurrent.atomic.AtomicInteger,
java.util.concurrent.atomic.AtomicIntegerArray,
java.util.concurrent.atomic.AtomicLong,
java.util.concurrent.atomic.AtomicLongArray
它们所做的正是我们想要的,而且有可能比同步版(synchronized)执行的更好:
private static final AtomicInteger nextNumber = new AtomicInteger();
public static int generateNumber() {
return nextNumber.getAndIncrement();
}
上一篇: window
下一篇: Struts2.0初步