为什么多线程会导致运算错误?

Real_csdn_User 2020-07-27 11:50:45
《Java编程思想(第4版)》 21.3.1 不正确地访问资源

这一节讲到多线程访问资源时会导致的错误。EvenGenerator.val的初始值为0,在EvenGenerator.next()里面对val执行了两次连续递增操作,并返回val的值。派生自Runnable接口的EvenChecker的run()方法检查EvenGenerator.next()的返回值是不是偶数。在程序中添加若干线程来产生EvenGenerator和EvenChecker,并用EvenChecker检查EvenGenerator产生的是不是偶数。运行程序,发现EvenGenerator居然会产生奇数,这是为什么?
//: concurrency/IntGenerator.java

public abstract class IntGenerator {
private volatile boolean canceled = false;

public abstract int next();

// Allow this to be canceled:
public void cancel() { canceled = true; }

public boolean isCanceled() { return canceled; }
}

//: concurrency/EvenChecker.java
import java.util.concurrent.*;

public class EvenChecker implements Runnable {
private IntGenerator generator;
private final int id;

public EvenChecker(IntGenerator g, int ident) {
generator = g;
id = ident;
}

public void run() {
while (!generator.isCanceled()) {
int val = generator.next();
if (val % 2 != 0) {
System.out.println(val + " not even!");
generator.cancel(); // Cancels all EvenCheckers
}
}
}

// Test any type of IntGenerator:
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();
}

// Default value for count:
public static void test(IntGenerator gp) {
test(gp, 10);
}
}

//: concurrency/EvenGenerator.java
// When threads collide.

public class EvenGenerator extends IntGenerator {
private int currentEvenValue = 0;

public int next() {
++currentEvenValue; // Danger point here!
Thread.yield();
++currentEvenValue;
return currentEvenValue;
}

public static void main(String[] args) {
EvenChecker.test(new EvenGenerator());
}
}
...全文
5225 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
qq_39936465 2020-07-29
  • 打赏
  • 举报
回复
引用 楼主 Real_csdn_User 的回复:
《Java编程思想(第4版)》 21.3.1 不正确地访问资源 这一节讲到多线程访问资源时会导致的错误。EvenGenerator.val的初始值为0,在EvenGenerator.next()里面对val执行了两次连续递增操作,并返回val的值。派生自Runnable接口的EvenChecker的run()方法检查EvenGenerator.next()的返回值是不是偶数。在程序中添加若干线程来产生EvenGenerator和EvenChecker,并用EvenChecker检查EvenGenerator产生的是不是偶数。运行程序,发现EvenGenerator居然会产生奇数,这是为什么?
因为多线程本身是异步运行的,如果多个线程共用一个数据,那么该数据在多个线程中会产生差异,造成结果的不可知性。要修正这个问题我们就需要设置这些线程对数据源进行同步,同步一般最常用的方法就是数据源进行加锁。
鸡窝里的毛 2020-07-28
  • 打赏
  • 举报
回复
这就叫一个和尚挑水吃,两个和尚抬水吃,三个和尚没水吃。就像窗口买票,大家一涌而上会是什么情况?很可能票已经卖光了,后边的人还往里边挤,最后只能扑了空。所以多线程对同一个变量进行操作的时候需要排队,也就是加锁,在某一瞬间这个变量是某个线程独有的,别的线程要用就得等待,就不会出问题了。
捏造的信仰 2020-07-28
  • 打赏
  • 举报
回复
在单个线程看来,++currentEvenValue 可能执行了两次,但要注意 currentEvenValue 这个变量是多个线程共享的,什么意思呢,就是多个线程同时访问同一个 EvenGenerator 对象,并发的调用 next() 方法,所以 next() 方法执行过程当中 ++currentEvenValue 可能执行了不止两次。
kimsung 2020-07-28
  • 打赏
  • 举报
回复
说白了, 多线程环境下不要使用类变量。 你把 56行放到next()方法里面就不会出这种问题了。 至于为什么不能使用类变量, 百度一大堆资料。

62,625

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧