在多线程运行下,对共享资源的访问要遵循互斥原则,否则会出现资源访问的错误。这时可以使用synchronized关键字对互斥的资源或是方法、对象进行加锁,实例如下:
首先在MyDataGenerator类中有一个偶数数据,以及一个布尔标志数据,代码如下:
public class MyDataGenerator {
private int evenData;
private boolean cancel;
MyDataGenerator() {
this.evenData = 0;
this.cancel = false;
}
public int nextEven() {
evenData ++; // 出错的位置
evenData ++;
return evenData;
}
public boolean getCancel() {
return cancel;
}
public int getEvenData() {
return evenData;
}
public void cancel() {
cancel = true;
System.out.println("Now the data is: " + evenData);
}
}
默认evenData是每次调用nextEven()方法后值加2,这样就一直使evenData为偶数。然后接下来创建一个用于测试的类MyDataChecker:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyDataChecker implements Runnable {
private MyDataGenerator myDataGenerator;
private int checkerId;
MyDataChecker(MyDataGenerator dg, int id) {
myDataGenerator = dg;
checkerId = id;
}
public void run() {
while (!myDataGenerator.getCancel()) {
if (myDataGenerator.nextEven() % 2 != 0) {
myDataGenerator.cancel();
System.out.println("Not even number, failure.");
}
}
}
public static void testDataFailure(MyDataGenerator myDataGenerator, int threadNum) {
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < threadNum; i++)
exec.execute(new MyDataChecker(myDataGenerator, i));
exec.shutdown();
}
}
在此类中启用多个MyDataChecker线程来执行DataGenerator中的nextEven()方法,在MyDataGenerator加入执行:
public static void main(String[] args) {
MyDataChecker.testDataFailure(new MyDataGenerator(), 10);
}
启用10个DataChecker线程,共同访问同一个Generator,此时因为nextEven()不是互斥访问,会存在一定的几率在两次evenData+1之间进程进行调度切换,从而evenData变成奇数。从而出现错误:
Now the data is: 1074
Not even number, failure.
这时在nextEven()方法前加入synchronized关键字可以使进程互斥访问此方法,在使用时加锁,从而不再出错:
public synchronized int nextEven() {
evenData ++;
evenData ++;
return evenData;
}