简体   繁体   中英

Is a static method containing loops thread-safe?

I have a huge loop that I wanted to split up into 4 threads. I've done so using a little bit noobish method(or maybe not?) and split up the counter of the for loops into 4 intervals, created a new Printwriter, and CrucibleOptimizer for each thread so that there are no conflicts, like this:

    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();
    }
}

And this is the method that I'm asking about. I don't know if its thread safe. I've been reading around and google says that as far as I don't have any local variables, I'm fine, but what concerns me is the multiple counters in those loops:

    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();
}

The "do not use static variables" advise is indeed too simplistic: the other requirement is to not pass shared objects to static methods running in different threads.

Loop counters and other primitive local variables are thread-safe. The only thing that could make a method non-thread safe is shared state. It appears that you have successfully avoided that by creating separate CrucibleOptimizer and PrintWriter objects.

One refactoring that I would attempt is combining your Runnable s. Make a named class that takes loop boundaries, and make four instances of that class in your main . This would work better than four separate anonymous classes that have very few differences:

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();
        }
    }
}

Now you can make four ThreadRunnable instances which share identical code.

Loops in of themselves are thread safe, so no you don't need to worry about that.

The only thing you need to worry about is anything that might be accessed by multiple threads at once.

However your entire architecture really needs some work.

For example why have 4 separate implementations for the runables rather than having one implementation and passing parameters into it to say which chunk to work on.

I also don't know what you are trying to do with all the loops but it's highly unlikely you really need any structure like that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM