【问题标题】:Java MultiThreading skips loop and gives wrong result [duplicate]Java多线程跳过循环并给出错误的结果[重复]
【发布时间】:2016-11-14 09:12:08
【问题描述】:

Java 多线程跳过循环并给出错误结果

package Threading;

class DemoThread extends Thread{   //Thread Class   
    static int count=0;   // variable incremented by both the threads

    public DemoThread(String name) {
        // TODO Auto-generated constructor stub
        super(name);
    }

    public void run() { 
        for(int i=0;i<100000;i++) {
            count++;
            System.out.println(Thread.currentThread()+"Count"+count);  // print thread operating on count variable
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }           
        }       
    }   
}


public class MyThreadClass {

    public static void main(String[] args) {
        // TODO Auto-generated method stub      
        DemoThread t1=new DemoThread("T1");
        DemoThread t2=new DemoThread("T2");
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();  //allowing both the threads to complee before main thread
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("Main Thread ends"+DemoThread.count);  //final value of count        
    }
}

count 的最终值应该是 199998,但它没有给出预期的结果。 为什么线程缺少循环???

【问题讨论】:

  • 请正确格式化您的代码。

标签: java multithreading


【解决方案1】:

这是因为线程 T1 和 T2 将同时更新 count(并发):

Thread[T1,5,main]Count10
Thread[T2,5,main]Count10
Thread[T1,5,main]Count12
Thread[T2,5,main]Count12
Thread[T2,5,main]Count14
Thread[T1,5,main]Count14
Thread[T1,5,main]Count15
Thread[T2,5,main]Count16

你应该使用AtomicInteger

并更新您的代码:

static int count=0;static AtomicInteger count= new AtomicInteger();

count++;count.incrementAndGet();

【讨论】:

  • 但是为什么......一次只有 1 个线程会进入可运行状态,即正确获取 CPU ???
  • 不,您需要了解有关可运行、线程和并行的更多信息。我觉得vogella.com/tutorials/JavaConcurrency/…是一篇好文章
  • 这真的很有帮助..我明白,这取决于我们可以并发的 CPU 内核,因为多个线程可能同时访问共享数据
【解决方案2】:

您必须使用 java.util.concurrent.atomic.AtomicInteger 而不是共享静态 int 变量。

【讨论】:

  • 你>可以AtomicInteger,但这不是唯一正确的解决方案。
【解决方案3】:

问题

在 Java 中,多线程为您的程序引入了异步行为,您必须在需要时强制执行同步。

没有同步,没有什么能阻止线程同时在同一个对象上调用同一个方法。这称为竞争条件,因为线程相互竞争以完成该方法。

程序的输出:

输出的第一行打印了 2!?这是因为 t1 写入了 count 的值并在打印之前被抢占。请注意,要抢占一个线程,它不需要进入睡眠状态。操作系统始终这样做。

如果您注意到第 1st 行和第 4th 行,您可以看到不一致之处。这种不一致在大型程序中变得不可预测。

这是多次运行的最终结果。

不应认为总是产生错误的结果。不,有时会产生正确的结果。这意味着结果将是不可预测的。


误会了吗?

线程跳过循环。!?不,线程没有跳过循环。不同的线程访问了相同的值,但在它可以写入自己的值之前,其他线程写入了该值并继续。结果下次访问该值时,它选择了错误的值。

这被称为Reader Writer Problem


解决方案

在您的程序中,t1t2 正在使用不同的值访问您的 count 变量。为了防止其他线程在其他线程完成之前调用 run(),我们在方法之前添加了一个 synchronized 关键字。

synchronized public void run(){}

synchronized 关键字保护状态免受竞争条件的影响。一次,一个线程进入同步方法,直到前一个线程退出该方法,其他线程才被允许使用该方法。

由于 synchronized 关键字,这是正确的输出。 注意:我使用 200 作为循环结束。


奖金

如果您正在使用将共享数据作为输入的专有方法。您可以使用同步

synchronized(objRef){
   //Method calling
}

objRef 是被同步对象的引用。


在 cmets 中推荐
[推荐解决方案]

您应该使用 AtomicInteger 而不是原生 int。使用java.util.concurrent.atomic 包。

Reference to java.util.concurrent.atomic

static AtomicInteger count = new AtomicInteger(0);代替static int count = 0;,用count.incrementAndGet();代替count++;

AtomicInteger 本质上是同步的。

Atomic Integer Reference Java Docs


【讨论】:

  • 冗长,但很好解释。但是,结论应该是使用 AtomicInteger (完全为了 OP 的目的而实现)而不是霰弹枪同步。
  • @SME_Dev 我已按照推荐添加了 AtomicInteger。
  • @SME_Dev 当然更好的解决方案是让两个线程都有自己的计数并将它们加在一起,从而消除了运行期间所有同步的需要
  • @bowmore 好吧,给定的例子当然是任意的。在 OP 的测试用例中,累计计数是打破外循环的谓词。在此总和等于限制之前,线程不知道它们是否可以终止并“添加结果”。所以你的想法就是知道总和的价值之后再进行总结,对吧?
  • @SME_Dev 是的,这个例子是任意的,我只是想说在某些情况下可以消除同步的需要,如果可能的话,这是最终的解决方案。它是否完全适用于 OP 的示例与我打算传达的内容无关。
猜你喜欢
  • 1970-01-01
  • 2017-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-29
  • 2016-03-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多