【问题标题】:Is a static method containing loops thread-safe?包含循环的静态方法是线程安全的吗?
【发布时间】:2014-08-09 11:19:40
【问题描述】:

我有一个很大的循环,我想分成 4 个线程。我已经这样做了,使用了一点点noobish方法(或者可能不是?)并将for循环的计数器分成4个间隔,为每个线程创建了一个新的Printwriter和CrucibleOptimizer,这样就不会发生冲突,如下所示:

    public static void main(String[] args) {
    Runnable run1 = new Runnable() {
        @Override
        public void run() {
            PrintWriter writer1;
            try {
                writer1 = new PrintWriter("test_result1.txt");
                CrucibleOptimizer optimizer1 = new CrucibleOptimizer();
                int[] loop1boundries = new int[]{1, 7};
                opt(optimizer1, writer1, loop1boundries[0], loop1boundries[1]);
            } catch (FileNotFoundException e) {
                System.out.println("File not found");
                e.printStackTrace();
            }
        }
    };
    Runnable run2 = new Runnable() {
        @Override
        public void run() {
            PrintWriter writer2;
            try {
                writer2 = new PrintWriter("test_result2.txt");
                CrucibleOptimizer optimizer2 = new CrucibleOptimizer();
                int[] loop2boundries = new int[]{8, 14};
                opt(optimizer2, writer2, loop2boundries[0], loop2boundries[1]);
            } catch (FileNotFoundException e) {
                System.out.println("File not found");
                e.printStackTrace();
            }
        }
    };
    Runnable run3 = new Runnable() {
        @Override
        public void run() {
            PrintWriter writer3;
            try {
                writer3 = new PrintWriter("test_result3.txt");
                CrucibleOptimizer optimizer3 = new CrucibleOptimizer();
                int[] loop3boundries = new int[]{15, 22};
                opt(optimizer3, writer3, loop3boundries[0], loop3boundries[1]);
            } catch (FileNotFoundException e) {
                System.out.println("File not found");
                e.printStackTrace();
            }
        }
    };
    Runnable run4 = new Runnable() {
        @Override
        public void run() {
            PrintWriter writer4;
            try {
                writer4 = new PrintWriter("test_result4.txt");
                CrucibleOptimizer optimizer4 = new CrucibleOptimizer();
                int[] loop4boundries = new int[]{23, 30};
                opt(optimizer4, writer4, loop4boundries[0], loop4boundries[1]);
            } catch (FileNotFoundException e) {
                System.out.println("File not found");
                e.printStackTrace();
            }
        }
    };
    Thread[] threads = new Thread[]{new Thread(run1), new Thread(run2), new Thread(run3), new Thread(run4)};
    for (Thread thr : threads){
        thr.start();
    }
}

这就是我要问的方法。我不知道它的线程是否安全。我一直在阅读,谷歌说,就我没有任何局部变量而言,我很好,但我担心的是这些循环中的多个计数器:

    public static void opt(CrucibleOptimizer opt, PrintWriter writer, int minIncluded, int maxIncluded){
    //more than this is never used
    final int oreMaterialsMaximum = 100;//100
    final int ingotMaterialMaximum = 30;//30

    //test for every possible material combination
    for (int a = minIncluded; a <= maxIncluded; a++){//for amount of ingots
        System.out.println("Testing for ingot number: " + a);
        double ratioMin = (Reference.UNITS_IMPOSSIBLE / (double)(a * Reference.UNITS_INGOT));
        for (int i = 0; i <= (int)(100 / Reference.UNITS_IMPOSSIBLE); i++){//for every ratio possible
            double currentRatio = round(i * ratioMin, 6);
            System.out.println("Testing for ratio: " + currentRatio);
            for (int b = 0; b <= ingotMaterialMaximum; b++){//with every amount of ingots
                for (int c = 0; c <= oreMaterialsMaximum; c++){//with every amount of rich ore
                    for (int d = 0; d <= oreMaterialsMaximum; d++){//with every amount of normal ore
                        for (int e = 0; e <= oreMaterialsMaximum; e++){//with every amount of poor ore
                            for (int f = 0; f <= oreMaterialsMaximum; f++){//with every amount of small ore
                                opt.set(null, null, null, a); //only the ingots are passed in this way
                                int[] res = opt.optimizeMaterial(new int[]{c, d, e, f, b}, currentRatio);
                                if (res != null){
                                    int units = 0;
                                    for (int j = 0; j < res.length; j++)
                                        units += res[j] * Reference.MATERIAL_UNITS[j];
                                    double unitsRight = Math.round(a * Reference.UNITS_INGOT * currentRatio);
                                    if (units != (int)unitsRight){ //if the units are not correct, log
                                        writer.println("I: " + a + " Rat: " + currentRatio + " I_av: " + b + " O_Ri: " + c + " O_No: " + d +
                                                " O_Po: " + e + " O_Sm: " + f + " units_wrong: " + units + " units_right: " + (int)unitsRight);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    System.out.println("Testing done");
    writer.close();
}

【问题讨论】:

  • 是什么让某些东西不是线程安全的? (提示:循环是无关紧要的,就像所有发布的代码一样,不包括违反上一个问题的答案的地方。)
  • 老兄..你必须再次检查你的设计..这么多循环?
  • 看代码就知道需要清理了。有很多代码重复,这是程序员最想避免的,而且你有 8 个循环重叠......即使你的算法需要它,也不是不创建一些中间方法来制作的理由代码可读。最后,当您说“我一直在阅读,谷歌说只要我没有任何局部变量,我很好”,实际上恰恰相反。局部变量不共享,因此不会带来任何风险。
  • @TheLostMind 是的,我知道,但我的算法实际上需要这么多参数。他们每个人都有一个循环。我真的不想花太多时间来编写这个程序,因为它旨在测试算法是否不会给出任何误报。我可以让它跳过几次迭代,但我不知道哪些迭代,它真的很复杂。
  • @jammaster - 你确定不能使用 recursion 吗?

标签: java multithreading loops


【解决方案1】:

“不要使用静态变量”的建议确实过于简单:另一个要求是不要将共享对象传递给在不同线程中运行的static 方法。

循环计数器和其他原始局部变量是线程安全的。唯一可以使方法非线程安全的是共享状态。您似乎通过创建单独的 CrucibleOptimizerPrintWriter 对象成功地避免了这种情况。

我会尝试的一个重构是结合你的Runnables。创建一个带循环边界的命名类,并在您的main 中创建该类的四个实例。这比四个几乎没有区别的独立匿名类更有效:

private static class ThreadRunnable implements Runnable {
    final String fileName;
    final int[] loopBoundaries;
    public ThreadRunnable(String fn, int[] lb) {
        fileName = fn;
        loopBoundaries = lb;
    }
    @Override
    public void run() {
        PrintWriter pw;
        try {
            pw = new PrintWriter(fileName);
            CrucibleOptimizer co = new CrucibleOptimizer();
            opt(co, pw, loop4boundries[0], loop4boundries[1]);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

现在您可以创建四个共享相同代码的ThreadRunnable 实例。

【讨论】:

  • 我知道它看起来很糟糕,但这只是一个一次性程序,所以我不介意它看起来像这样。谢谢你的回答。
【解决方案2】:

循环本身是线程安全的,所以不用担心。

您唯一需要担心的是任何可能被多个线程同时访问的东西。

但是您的整个架构确实需要一些工作。

例如,为什么要为可运行对象提供 4 个单独的实现,而不是只有一个实现并将参数传递给它来说明要处理哪个块。

我也不知道你想对所有循环做什么,但你不太可能真的需要这样的结构。

【讨论】:

  • 是的,我知道它看起来很糟糕,但这只是一次性使用程序。所有循环都是为了测试可以传递给 CrucibleOptimizer 的每个可能的参数组合。我本可以让它跳过一些无用的迭代,但这很好,特别是因为我不介意这个程序运行几个小时。
猜你喜欢
  • 2010-11-08
  • 2016-07-16
  • 1970-01-01
  • 1970-01-01
  • 2013-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多