首页 > 解决方案 > Forcing Code to wait for condition using while loop requires thread.sleep()

问题描述

I've created a program that divides a linear search (searches for -1) up onto 4 separate threads.

public class main {

    static boolean found = false;

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        int threadCount = 4; //amount of threads to use
        Random rand = new Random();
        Searcher[] s_arr = new Searcher[threadCount]; //array of threads


        int[] arr = new int[10000]; //array to search through
        for (int i = 0; i < arr.length; i++) //randomizing #'s in array
            arr[i] = (int) (rand.nextFloat() * 1000);

        int randIndex = rand.nextInt(arr.length); //choose random index

        arr[randIndex] = -1; //set random index to = -1

        for (int i = 0; i < threadCount; i++) { //
            s_arr[i] = new Searcher(Arrays.copyOfRange(arr, i * (arr.length/threadCount), (i+1) * (arr.length/threadCount)), 
                    (int) (i), i); //assign subarray for this thread to search through
            System.out.println(s_arr[i].wait);
            s_arr[i].start(); 
        }


        //CODE IN QUESTION HERE ----------------------------
        //while (!found) ;

        while (!found) //wait until value is found
        {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
//-----------------------------------------------------------





        System.out.println("found!");

        for (int i = 0; i < threadCount; i++) {
            try {
                s_arr[i].join(); //wait for the threads in order before continuing
                System.out.println("Thread ["+i+"] completed");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("All threads stopped, program complete.");
    }

}

public class Searcher extends Thread {

    int[] arr;
    int wait;
    int index;

    public Searcher(int[] arr, int wait, int i) {
        this.arr = arr;
        this.wait = wait;
        this.index = i;
    }

    @Override
    public void run() {
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == -1) {
                System.out.println("["+index+"] -1 Found at index: "+i);
                main.found = true;
                break;
            }
            if (main.found) break;
            //purposely slow down this thread
            try {
                Thread.sleep(wait);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("["+index+"] has stopped");

    }

}

I've marked out the code in question, while using the first (commented out) while loop, the program doesn't move beyond that point, but if I switch and use the other while loop right below it (the one that forces it to wait 1 millisecond each iterator) the program works just fine.

Why is this and is there a more efficient/practical way to accomplish this task?

标签: javamultithreading

解决方案


在空循环语句的条件下重复读取非易失性字段可能会导致无限循环,因为编译器优化可能会将此字段访问移出循环。

资源:help.semmle.com

如果替换static boolean found = false;volatile static boolean found = false;,第一个循环将起作用,但我不推荐它,因为它会浪费您的 CPU 时间。您应该考虑使用waitand notify

下面static boolean found,添加static final Object lock = new Object();和替换两个while循环

try {
    synchronized (lock) {
        // we will wait here until we get notified
        lock.wait();
    }
} catch (InterruptedException e) {
    e.printStackTrace();
}

main.found = true添加后也是

synchronized (main.lock) {
    main.lock.notify();
}

最后,您的代码应如下所示

public class main {

    static boolean found;
    static final Object lock = new Object();

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        int threadCount = 4; //amount of threads to use
        Random rand = new Random();
        Searcher[] s_arr = new Searcher[threadCount]; //array of threads


        int[] arr = new int[10000]; //array to search through
        for (int i = 0; i < arr.length; i++) //randomizing #'s in array
            arr[i] = (int) (rand.nextFloat() * 1000);

        int randIndex = rand.nextInt(arr.length); //choose random index

        arr[randIndex] = -1; //set random index to = -1

        for (int i = 0; i < threadCount; i++) { //
            s_arr[i] = new Searcher(Arrays.copyOfRange(arr, i * (arr.length/threadCount), (i+1) * (arr.length/threadCount)),
                    (int) (i), i); //assign subarray for this thread to search through
            System.out.println(s_arr[i].wait);
            s_arr[i].start();
        }

        try {
            synchronized (lock) {
                lock.wait();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("found!");

        for (int i = 0; i < threadCount; i++) {
            try {
                s_arr[i].join(); //wait for the threads in order before continuing
                System.out.println("Thread ["+i+"] completed");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("All threads stopped, program complete.");
    }

}

class Searcher extends Thread {

    int[] arr;
    int wait;
    int index;

    public Searcher(int[] arr, int wait, int i) {
        this.arr = arr;
        this.wait = wait;
        this.index = i;
    }

    @Override
    public void run() {
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == -1) {
                System.out.println("["+index+"] -1 Found at index: "+i);
                main.found = true;
                synchronized (main.lock) {
                    main.lock.notify();
                }
                break;
            }
            if (main.found) break;
            //purposely slow down this thread
            try {
                Thread.sleep(wait);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("["+index+"] has stopped");

    }

}

推荐阅读