这两种并发实现哪个更好更快
Which one of these two concurrent implementations is a better faster
我有两个并行生成素数的实现。核心代码取自 Whosebug 中的另一个 post。
我想知道这些实现中的哪一个是首选,为什么?另外,是否有更好更快的解决方案?
实施 1:
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class PrimeThreads {
private static int currentPrime = 0;
public static void main(String[] args) {
Object lock = new Object();
Thread primerGenThread = new Thread(() -> {
String threadName = Thread.currentThread().getName();
System.out.println("Starting thread: " + threadName);
int currentPrimeNo = 0;
synchronized (lock) {
try {
currentPrimeNo = generateNextPrime();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Prime Number Associated with this thread " + threadName + " is: " + currentPrimeNo);
System.out.println("Completed thread: " + threadName);
});
System.out.println("****This is where the project starts*****");
Scanner reader = new Scanner(System.in);
System.out.print("Enter number of threads you want to create: ");
int n = reader.nextInt();
reader.close();
ExecutorService executor = Executors.newFixedThreadPool(n);
for(int i=1;i<=n; i++) {
executor.submit(primerGenThread);
}
executor.shutdown();
try {
executor.awaitTermination(10, TimeUnit.MINUTES);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("****This is where the project ends*****");
}
private static int generateNextPrime() throws InterruptedException {
long startTime = System.nanoTime();
currentPrime++;
if (currentPrime < 2) {
currentPrime = 2;
return currentPrime;
}
for (int i = 2; i < currentPrime; i++) {
if (currentPrime % i == 0) {
currentPrime++;
i = 2;
} else {
continue;
}
}
long endTime = System.nanoTime();
System.out.println("Time taken: " + (endTime - startTime) + " naoseconds.");
return currentPrime;
}
}
和实施 2:
import java.util.Scanner;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class PrimeAsyncThreads {
private static int currentPrime = 0;
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 threads you want to create: ");
int n = reader.nextInt();
reader.close();
ExecutorService executor = Executors.newFixedThreadPool(n);
for (int i = 1; i <= n; i++) {
CompletableFuture.supplyAsync(() -> {
try {
return generateNextPrime();
} catch (InterruptedException e) {
e.printStackTrace();
}
return n;
}, executor).thenAccept(s -> System.out.println("Prime Number Associated with this thread "
+ Thread.currentThread().getName() + " is: " + currentPrime));
}
executor.shutdown();
try {
executor.awaitTermination(10, TimeUnit.MINUTES);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("****This is where the project ends*****");
}
private static int generateNextPrime() throws InterruptedException {
long startTime = System.nanoTime();
currentPrime++;
if (currentPrime < 2) {
currentPrime = 2;
return currentPrime;
}
for (int i = 2; i < currentPrime; i++) {
if (currentPrime % i == 0) {
currentPrime++;
i = 2;
} else {
continue;
}
}
long endTime = System.nanoTime();
System.out.println("Time taken: " + (endTime - startTime) + " naoseconds.");
return currentPrime;
}
}
感谢您的建议和帮助。
编辑:
还注意到第二个实现不保证每个线程都会得到一个新的素数。在这种情况下,有时多个线程会获得相同的 currentPrime 变量值。
谢谢。
这些实现之间的主要区别在于它们的执行方式。
实现1基本等于顺序执行。使用线程没有任何优势,因为如何使用同步块。
在生成下一个素数之前,每个线程都等待前一个线程完成。
您已经注意到实现 2 多次计算同一个素数。这是因为没有同步。只有计数器 currentPrime
用于以某种方式控制下一个线程中应将哪个数字视为质数。
因此,这两种实现都无法并行计算素数以产生可行的结果。
想想套路。您使用一个值来确定它是否是质数。这个值应该是每个线程进行计算的输入。
现在唯一要考虑的是如何让这个值线程安全,确保它只被使用一次。
这可以实现,例如通过为 currentPrime
.
使用原子变量
另一个改进可能是在 generateNextPrime()
方法之外增加 currentPrime
。此方法可以将值作为参数。像
generateNextPrime(currentPrime.incrementAndGet());
我有两个并行生成素数的实现。核心代码取自 Whosebug 中的另一个 post。
我想知道这些实现中的哪一个是首选,为什么?另外,是否有更好更快的解决方案?
实施 1:
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class PrimeThreads {
private static int currentPrime = 0;
public static void main(String[] args) {
Object lock = new Object();
Thread primerGenThread = new Thread(() -> {
String threadName = Thread.currentThread().getName();
System.out.println("Starting thread: " + threadName);
int currentPrimeNo = 0;
synchronized (lock) {
try {
currentPrimeNo = generateNextPrime();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Prime Number Associated with this thread " + threadName + " is: " + currentPrimeNo);
System.out.println("Completed thread: " + threadName);
});
System.out.println("****This is where the project starts*****");
Scanner reader = new Scanner(System.in);
System.out.print("Enter number of threads you want to create: ");
int n = reader.nextInt();
reader.close();
ExecutorService executor = Executors.newFixedThreadPool(n);
for(int i=1;i<=n; i++) {
executor.submit(primerGenThread);
}
executor.shutdown();
try {
executor.awaitTermination(10, TimeUnit.MINUTES);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("****This is where the project ends*****");
}
private static int generateNextPrime() throws InterruptedException {
long startTime = System.nanoTime();
currentPrime++;
if (currentPrime < 2) {
currentPrime = 2;
return currentPrime;
}
for (int i = 2; i < currentPrime; i++) {
if (currentPrime % i == 0) {
currentPrime++;
i = 2;
} else {
continue;
}
}
long endTime = System.nanoTime();
System.out.println("Time taken: " + (endTime - startTime) + " naoseconds.");
return currentPrime;
}
}
和实施 2:
import java.util.Scanner;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class PrimeAsyncThreads {
private static int currentPrime = 0;
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 threads you want to create: ");
int n = reader.nextInt();
reader.close();
ExecutorService executor = Executors.newFixedThreadPool(n);
for (int i = 1; i <= n; i++) {
CompletableFuture.supplyAsync(() -> {
try {
return generateNextPrime();
} catch (InterruptedException e) {
e.printStackTrace();
}
return n;
}, executor).thenAccept(s -> System.out.println("Prime Number Associated with this thread "
+ Thread.currentThread().getName() + " is: " + currentPrime));
}
executor.shutdown();
try {
executor.awaitTermination(10, TimeUnit.MINUTES);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("****This is where the project ends*****");
}
private static int generateNextPrime() throws InterruptedException {
long startTime = System.nanoTime();
currentPrime++;
if (currentPrime < 2) {
currentPrime = 2;
return currentPrime;
}
for (int i = 2; i < currentPrime; i++) {
if (currentPrime % i == 0) {
currentPrime++;
i = 2;
} else {
continue;
}
}
long endTime = System.nanoTime();
System.out.println("Time taken: " + (endTime - startTime) + " naoseconds.");
return currentPrime;
}
}
感谢您的建议和帮助。
编辑: 还注意到第二个实现不保证每个线程都会得到一个新的素数。在这种情况下,有时多个线程会获得相同的 currentPrime 变量值。
谢谢。
这些实现之间的主要区别在于它们的执行方式。
实现1基本等于顺序执行。使用线程没有任何优势,因为如何使用同步块。 在生成下一个素数之前,每个线程都等待前一个线程完成。
您已经注意到实现 2 多次计算同一个素数。这是因为没有同步。只有计数器 currentPrime
用于以某种方式控制下一个线程中应将哪个数字视为质数。
因此,这两种实现都无法并行计算素数以产生可行的结果。
想想套路。您使用一个值来确定它是否是质数。这个值应该是每个线程进行计算的输入。 现在唯一要考虑的是如何让这个值线程安全,确保它只被使用一次。
这可以实现,例如通过为 currentPrime
.
另一个改进可能是在 generateNextPrime()
方法之外增加 currentPrime
。此方法可以将值作为参数。像
generateNextPrime(currentPrime.incrementAndGet());