从所有线程更新共享变量

时间:2018-03-15 21:50:53

标签: java multithreading volatile java-threads thread-synchronization

我是多线程的新手。我有一个volatile变量currentPrimeNo,它将打印每个新线程在run方法中实现的下一个素数。但每次我为每个线程获得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);
    }

}

主要课程是:

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类的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

****这是项目结束的地方*****

3 个答案:

答案 0 :(得分:0)

澄清问题后:

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

b)没有得到一个接一个生成的素数 - 这是因为计算从多个线程同时开始,并且这些线程并行工作(这是什么合并执行人确实)。

要强制所有任务按顺序运行,请使用newSingleThreadExecutor。

// 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*****");
}

预期输出产生为:

  

****这是项目启动的地方*****输入您要创建的进程数:10启动进程ID:1 Prime Number Associated   与此线程是:2完成进程ID:1启动进程ID:2   与此线程关联的素数是:3已完成的进程ID:2   启动进程id:3与此线程关联的Prime数字为:5   已完成的进程ID:3正在启动进程ID:4 Prime Number Associated   这个线程是:7完成进程id:4启动进程id:5   与此线程关联的素数是:11已完成的进程ID:   5启动进程id:6与此线程关联的Prime Number是:   13已完成的进程ID:6启动进程ID:7 Prime Number   与此线程相关联:17已完成进程ID:7正在启动   进程ID:8与此线程关联的素数是:19   已完成的进程ID:8正在启动进程ID:9 Prime Number Associated   使用此主题是:23已完成进程ID:9正在启动进程ID:   与此主题相关联的10个素数是:29已完成的过程   id:10

请注意,由于执行实际上是序列化的,因此在这里使用执行程序(或单独的执行线程)将无法获得性能提升。

可以从并行执行中受益的最佳问题是输入可以被分割,然后由多个线程并行处理,最后再次组合的问题。例如,将位图图片转换为黑白图像是并行执行的一个很好的问题,因为位图可以切成8个部分,并且这些部分被送到8个并行运行的线程。最后,一旦完成所有线程,代码就可以将输出汇编到一张图片中,并从8倍的执行速度中受益。

答案 1 :(得分:0)

由于您尚未在此处共享generateNextPrime的代码,因此要指出代码确实失败的位置有点困难。

存在与此相关的固有问题。

在添加了Util.generateNextPrime()之后编辑。

当我们使用volatile关键字时,所有线程都会看到当前值,而不是变量的缓存值。但是在您的代码中,volatile变量是在Runnable实现中定义的。因此,它不为此目的服务。确实,run方法调用了generateNextPrime并传递了volatile变量,但被调用的方法实际上看到和处理的是 副本 变量而不是 精确 变量(通过参考传递更多关于传递值和传递参考将更有助于更好地理解这一点)。这里的目标是有一个变量,其值应该由generateNextPrime调用改变,这将由每个线程在运行时完成。

我将currentPrimeNo定义移到Util类,以便所有线程只能看到 一个 变量(而不是它的副本)和这也是volatile变量的实时值。为了紧凑性,方法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*****

答案 2 :(得分:0)

查看输出时,您可以看到所有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:您也可以像下面那样更改您的Processor类,而无需同步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);
    }
}