【问题标题】:Mutiple Threads: the application doesn't produce same result every time多线程:应用程序每次都不会产生相同的结果
【发布时间】:2013-09-02 11:24:13
【问题描述】:

所以在这里我编写了三个简单的类来检查多个线程在 java 中是如何工作的,但是每次运行时它们都会产生不同的结果。代码如下:

        public class Accum {

        private static Accum a = new Accum();
        private int counter = 0;

        private Accum(){}

        public static Accum getAccum(){
            return a;
        }

        public void updateCounter(int add){
            counter+=add;
        }

        public int getCount(){
            return counter;
        }
    }//end of class

        public class ThreadOne implements Runnable {
        Accum a = Accum.getAccum();
        public void run() {
            for(int x=0; x<98; x++){
                //System.out.println("Counter in TWO "+a.getCount());
                a.updateCounter(1000);
                try{
                    Thread.sleep(50);
                }catch(InterruptedException ex){}
            }
            System.out.println("one " + a.getCount());
        }
    }//end of class

        public class ThreadTwo implements Runnable{
        Accum a = Accum.getAccum();
        public void run() {
            for(int x=0; x<99; x++){
                //System.out.println("counter in Two "+a.getCount());
                a.updateCounter(1);
                try{
                    Thread.sleep(50);
                }catch(InterruptedException ex){}
            }
            System.out.println("two "+a.getCount());
    }

        public class TestThreaad {
        public static void main(String[]args){
            ThreadOne t1 = new ThreadOne();
            ThreadTwo t2 = new ThreadTwo();

            Thread one = new Thread(t1);
            Thread two = new Thread(t2);

            one.start();
            two.start();
        }
    }end of class

所以预期的结果是:一个98098,两个98099,但结果结果只是无法预测,有时是78000或81000,我不知道..

但如果我添加一些代码来打印一行当前计数值,最终结果将是正确的..

实在不知道怎么回事,竟然在ThreadOne和ThreadTwo,run()方法中加上了关键字synchronized,问题依旧……

我已经研究了 3 个月的 java,这是我遇到过的最难以捉摸的问题......所以提前感谢任何人可以帮助我理解多线程的基本点......

【问题讨论】:

标签: java multithreading


【解决方案1】:

代码未同步。由于它是不同步的,不同的Thread 尝试更新计数器可能同时导致此问题。

如果您同步了updateCounter,那么此方法的访问权限将是正确的。

public synchronized void updateCounter(int add){
      counter+=add;
}

【讨论】:

  • 非常感谢,我正在看的书是Head First Java 2dn Edition,上面说如果我写像i++,i+=100这样的子句,它会被认为是原子的......
  • java中有java.util.concurrent.atomic.AtomicInteger可用
  • @user2548760,我相信那本书是错的。例如,请参阅stackoverflow.com/questions/15287419/is-i-atomic-in-java。 JLS 可能是最终的来源。该操作是原子的,仅在整个结果一次存储的意义上,而不是相对于其他线程的操作安全地执行操作的意义上。
  • 把那本书扔掉。阅读docs.oracle.com/javase/tutorial/essential/concurrency/… 和该教程的其余部分。
【解决方案2】:

在您的示例中,Accum 实例在线程之间共享。你的更新过程是一个典型的 READ、COMPUTE-UPDATE、WRITE 操作序列。由于资源是共享的且不受保护,这三个操作(来自两个线程 - 进行六个操作)可以以多种不同方式交错,从而导致更新丢失。

以下是操作顺序的示例(数字表示线程):

READ #1           -> reads 10
COMPUTE-UPDATE #1 -> computes 1010
READ #2           -> reads 10
WRITE #1          -> writes 1010
COMPUTE-UPDATE #2 -> computes 11
WRITE #2          -> writes 11 (earlier update is lost)

所以你看,几乎任何结果都是可能的。正如@SubhrajyotiMajumder 所说,您可以使用synchronized 来修复它。但如果你这样做,也许线程不适合你的问题;或者,您需要另一个算法过程。

【讨论】:

    【解决方案3】:

    您的代码未正确同步。 作为同步方法的替代方法,我建议使用AtomicInteger 来存储从不同线程访问和修改的变量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多