从所有线程更新共享变量

Update a shared variable from all the threads

我是多线程新手。我有一个 volatile 变量 currentPrimeNo,它会为每个新线程打印 运行 方法中实现的下一个质数。但是每次我将每个线程的 currentPrimeNo 设置为 0 时。我应该如何保持全局变量 currentPrimeNo 更新?

public class Processor implements Runnable {
    private int id;
    private static volatile int currentPrimeNo = 0;

    public Processor(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        System.out.println("Starting process id: " + id);
        currentPrimeNo = Utils.generateNextPrime(currentPrimeNo);
        System.out.println("Prime Number Associated with this thread is: " + currentPrimeNo);
        System.out.println("Completed process id: " + id);
    }

}

而主要的 class 是:

public class MainClass {

    @SuppressWarnings("resource")
    public static void main(String[] args) {
        System.out.println("****This is where the project starts*****");
        Scanner reader = new Scanner(System.in);
        System.out.print("Enter number of processes you want to create: ");
        int n = reader.nextInt();
        ExecutorService executor = Executors.newFixedThreadPool(n);
        for(int i=1;i<=n; i++) {
            executor.submit(new Processor(i));
        }
        executor.shutdown();
        try {
            executor.awaitTermination(10, TimeUnit.MINUTES);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        System.out.println("****This is where the project ends*****");
    }
}

下面是 Util class:

的 generateNextPrime 方法
public synchronized static int generateNextPrime(int currentPrime) {
        int nextPrime = 2;
        if (currentPrime <= 1) {
            return nextPrime;
        } else {
            for (int i = currentPrime + 1;; i++) {
                boolean notPrime = false;
                for (int j = 2; j < i; j++) {
                    if (i % j == 0) {
                        notPrime = true;
                        break;
                    }
                }
                if (notPrime == false) {
                    return i;
                }
            }
        }
    }

下面是我得到的输出:

****这是项目开始的地方*****

输入您要创建的进程数:4

启动进程 ID:2

启动进程 ID:3

启动进程 ID:1

启动进程 ID:4

与此线程关联的素数是:2

与此线程关联的素数是:2

已完成的进程 ID:4

已完成的进程 ID:1

与此线程关联的素数是:2

完成的进程id:2

与此线程关联的素数是:2

已完成的进程 ID:3

****项目到此结束*****

弄清问题后:

a) 结果为零 - 事实上并非如此,这次我实际上 运行 代码 :) 它 returns 完全随机的结果符合预期,具体取决于创建的线程数。 (请注意,您使用的是 threads,而不是 processes。) 随机结果的原因是每个线程实例都从其他线程实例设置的值开始。由于执行顺序不确定,因此输出也不确定。

b) 没有一个接一个地生成素数 - 这是因为计算从多个线程开始同时,并且这些线程并行工作(这就是汇集的执行者确实如此)。

要按顺序强制执行所有任务 运行,请使用新的 SingleThreadExecutor。

// final ExecutorService executor = Executors.newFixedThreadPool(n); // this uses a pool
final ExecutorService executor = Executors.newSingleThreadExecutor(); // this is sequential 


public static void main(String[] args) throws InterruptedException {
    System.out.println("****This is where the project starts*****");
    final Scanner reader = new Scanner(System.in);
    System.out.print("Enter number of processes you want to create: ");
    final int n = reader.nextInt();
    // final ExecutorService executor = Executors.newFixedThreadPool(n); // this uses a pool
    final ExecutorService executor = Executors.newSingleThreadExecutor(); // this uses a single thread

    for(int i=1;i<=n; i++) {
        executor.submit(new Processor(i));
    }
    executor.awaitTermination(10, TimeUnit.MINUTES);
    System.out.println("****This is where the project ends*****");
}

预期输出为:

****This is where the project starts***** Enter number of processes you want to create: 10 Starting process id: 1 Prime Number Associated with this thread is: 2 Completed process id: 1 Starting process id: 2 Prime Number Associated with this thread is: 3 Completed process id: 2 Starting process id: 3 Prime Number Associated with this thread is: 5 Completed process id: 3 Starting process id: 4 Prime Number Associated with this thread is: 7 Completed process id: 4 Starting process id: 5 Prime Number Associated with this thread is: 11 Completed process id: 5 Starting process id: 6 Prime Number Associated with this thread is: 13 Completed process id: 6 Starting process id: 7 Prime Number Associated with this thread is: 17 Completed process id: 7 Starting process id: 8 Prime Number Associated with this thread is: 19 Completed process id: 8 Starting process id: 9 Prime Number Associated with this thread is: 23 Completed process id: 9 Starting process id: 10 Prime Number Associated with this thread is: 29 Completed process id: 10

请注意,由于执行实际上是序列化的,因此在此处使用执行程序(或单独的执行线程)不会提高性能。

可以从并行执行中获益的最佳问题是可以拆分输入,然后由多个线程并行处理,最后再次组合的问题。例如,将位图图片转换为 black-and-white 对于并行执行来说是一个很好的问题,因为位图可以切成 8 块,这些块并行地馈送到 8 个线程 运行ning。最后,一旦所有线程都完成,代码就可以将输出组装成一张图片,并受益于 8 倍的执行速度。

由于您尚未在此处共享 generateNextPrime 的代码,因此很难指出代码的具体错误位置。

有一个与此相关的固有问题。

添加Util.generateNextPrime()后编辑。

当我们使用 volatile 关键字时,所有线程都会看到当前值而不是变量的缓存值。但是在您的代码中, volatile 变量是在 Runnable 实现中定义的。因此,它不用于此目的。 run 方法确实调用了 generateNextPrime 并传递了 volatile 变量,但被调用的方法实际看到和处理的是 copy[=变量的 52=] 而不是 exact 变量(阅读更多关于按值传递与按引用传递的内容将是有益的更好地理解这一点)。此处的目标是拥有一个变量,其值应由 generateNextPrime 调用更改,该调用将在 运行 时由每个线程完成。

我将 currentPrimeNo 定义移动到 Util class 以便所有线程只看到 one 变量(而不是它的副本)以及 volatile 变量的 real-time 值。为了紧凑起见,方法 generateNextPrime() 也做了一些改动。输出不一定需要按照相同的顺序,因为您不知道工作线程的调用顺序。

代码如下:

public class Processor implements Runnable {
    private int id;

    public Processor(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        System.out.println("Starting process id: " + id);
        int currentPrimeNo = Utils.generateNextPrime();
        System.out.println("Prime Number Associated with this thread " + id +" is: " + currentPrimeNo);
        System.out.println("Completed process id: " + id);
    }

}

public class Utils {

    private static volatile int currentPrime = 0;
    public static synchronized int generateNextPrime(){
        currentPrime++;
        if(currentPrime < 2){
            currentPrime = 2;
            return currentPrime;
        }
        for (int i = 2; i <currentPrime; i++) {
            if(currentPrime%i == 0) {
                currentPrime++;
                i=2;
            } else{
                continue;
            }
        }
        return currentPrime;
    }
}

基准测试时看到的输出

示例 1:

****This is where the project starts*****
Enter number of processes you want to create: 4
Starting process id: 3
Starting process id: 1
Starting process id: 2
Starting process id: 4
Prime Number Associated with this thread 3 is: 2
Prime Number Associated with this thread 1 is: 7
Completed process id: 1
Prime Number Associated with this thread 2 is: 3
Completed process id: 2
Prime Number Associated with this thread 4 is: 5
Completed process id: 3
Completed process id: 4
****This is where the project ends*****

示例 2:

****This is where the project starts*****
Enter number of processes you want to create: 6
Starting process id: 5
Starting process id: 1
Starting process id: 3
Starting process id: 2
Starting process id: 4
Prime Number Associated with this thread 2 is: 7
Prime Number Associated with this thread 4 is: 11
Completed process id: 4
Prime Number Associated with this thread 1 is: 3
Completed process id: 1
Prime Number Associated with this thread 5 is: 5
Completed process id: 5
Prime Number Associated with this thread 3 is: 2
Starting process id: 6
Completed process id: 2
Prime Number Associated with this thread 6 is: 13
Completed process id: 6
Completed process id: 3
****This is where the project ends*****

查看输出时,您可以看到所有 4 个线程都在任何计算之前启动。质数是在线程启动后立即计算的,因此很可能所有线程都以相同的起始质数 (0) 开始,因此以相同的结束数 (2) 结束。因此,您得到的输出是有意义的。

您的代码最大的问题是您并行执行计算,但期望按顺序得到结果。要获得所需的输出,您可以将 Utils.generateNextPrime(currentPrimeNo) 方法调用包装在同步块中。这将确保一次只有一个线程可以对主要值执行操作。

更新 1:这是我在 运行 你的代码时得到的输出:

****This is where the project starts*****
Enter number of processes you want to create: 4
Starting process id: 2
Prime Number Associated with this thread is: 2
Completed process id: 2
Starting process id: 1
Prime Number Associated with this thread is: 3
Completed process id: 1
Starting process id: 4
Prime Number Associated with this thread is: 5
Completed process id: 4
Starting process id: 3
Prime Number Associated with this thread is: 7
Completed process id: 3
****This is where the project ends*****

更新 2:您还可以像下面这样更改处理器 class,无需同步 generateNextPrime 方法:

public class Processor implements Runnable {
    private static Object lock = new Object();
    private int id;
    private static volatile int currentPrimeNo = 0;

    public Processor(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        System.out.println("Starting process id: " + id);
        synchronized (lock) {
            currentPrimeNo = generateNextPrime(currentPrimeNo);
        }
        System.out.println("Prime Number Associated with this thread is: " + currentPrimeNo);
        System.out.println("Completed process id: " + id);
    }
}