首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >带有睡眠间隔的Java Thread join()导致问题

带有睡眠间隔的Java Thread join()导致问题
EN

Stack Overflow用户
提问于 2018-10-10 17:41:16
回答 4查看 145关注 0票数 2

我正在做并发编程的教程,我看到.join()方法被用来确保线程将运行到结束,然后再继续其余的代码。

但是,当我尝试使用2个线程递增和递减一个变量100次(初始值: 2000)时,最终结果应该显示为2000,但它显示大于2000或小于2000。只有当我在main方法中取消对Thread.sleep的注释时,它才能正常工作并始终显示2000

代码语言:javascript
复制
public class Main {
    public static void main(String[] args) {
        SharedObj sharedObj = new SharedObj();
        Thread thread1 = new Thread(new ThreadMinus(sharedObj));
        Thread thread2 = new Thread(new ThreadPlus(sharedObj));
        thread1.start();
//        try {
//            Thread.sleep(100);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("number is: " + sharedObj.num);
        System.out.println("increment count: " + sharedObj.countPos);
        System.out.println("decrement count: " + sharedObj.countNeg);

    }
}

class SharedObj {
    public int num;
    public int countPos = 0;
    public int countNeg = 0;

    public SharedObj() {
        num = 2000;
    }

    public void change(int x) {
        num += x;
        if (x < 0) {
            countNeg++;
        } else {
            countPos++;
        }
        System.out.println("number is: " + num + " with operation: " + x);
    }
}

class ThreadMinus implements Runnable {
    SharedObj sharedObj;

    public ThreadMinus(SharedObj sharedObj) {
        this.sharedObj = sharedObj;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            sharedObj.change(-1);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class ThreadPlus implements Runnable {
    SharedObj sharedObj;

    public ThreadPlus(SharedObj sharedObj) {
        this.sharedObj = sharedObj;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            sharedObj.change(+1);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2018-10-10 17:51:59

这不是睡眠/连接导致的问题。这是竞争条件,即您在不保护同一变量的情况下递增该变量。将synchronized添加到SharedObj#change中,就可以了:

代码语言:javascript
复制
public synchronized void change(int x) { //...
票数 2
EN

Stack Overflow用户

发布于 2018-10-13 03:34:04

您看到的问题是一个典型的竞态条件。考虑一下num += x;,它看起来像一个操作,但实际上它是CPU中的多个操作。

简单来说就是1. temp1 = num;2. temp2 = x;3. temp3 = temp1 + temp2 4. num = temp3

它实际上发生在CPU的寄存器中,所以当您编写Java时,您不会明显地看到它。

当您有多个线程同时运行并且正在执行这些指令时,您可能会得到意外的结果。

例如,让我们从x=1和num = 2开始。

线程A正在执行:

代码语言:javascript
复制
 1. temp1a = num; 
 2. temp2a = x; 
 3. temp3a = temp1a + temp2a; 
 4. num = temp3a; 

线程B正在执行

代码语言:javascript
复制
 1. temp1b = num;
 2. temp2b = x;
 3. temp3b = temp1b + temp2b;
 4. num = temp3b;

现在,如果它们同时执行这些操作,您可能会得到以下命令:

代码语言:javascript
复制
 1. temp1a = num; //2
 2. temp2a = x; //1
 3. temp3a = temp1a + temp2a; //3

 4. temp1b = num; //2
 5. temp2b = x; //1
 6. temp3b = temp1b + temp2b; //3
 7. num = temp3b; //3

 8. num = temp3a; //3

因此,尽管您将num递增了两次,但您得到的结果是3,而不是4。这是一个竞争条件。有许多方法可以避免它,同步是其中之一,但还有其他技术。

如果你想更好地理解它,我真的推荐这个在线课程。https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY

它非常便宜,你可以在几个小时内完成它,但它通过示例很好地解释了这一点,绝对是从头开始的,并且它进入了关于并发性的非常高级的主题。这是一个很好的时间投资。

在您的示例中,只需像这样添加synchronized:

代码语言:javascript
复制
public synchronized void change(int x) {
    num += x;
    if (x < 0) {
        countNeg++;
    } else {
        countPos++;
    }
    System.out.println("number is: " + num + " with operation: " + x);
}

将会解决问题,但它不是灵丹妙药。您应该花点时间来理解为什么在这种情况下它可以解决问题,而在另一种情况下可能无法解决问题。

我希望它能帮上忙。

票数 2
EN

Stack Overflow用户

发布于 2018-10-10 18:45:44

发生这种情况是因为您正在尝试并发更新同一变量的值。通过观察输出,您可以发现,在某些情况下,由于这种并发性,程序无法更新值。您可以通过将方法“change”设置为“synchronized”来解决此问题,或者通过增加每个线程在Main方法中保持睡眠的睡眠时间来解决此问题。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52737179

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档