需求
Prime Finder
For this homework assignment, you must:
Add a pending variable to the WorkQueue class to track unfinished work, and implement a finish() method that waits until there is no more pending work.
Implement the multithreaded findPrimes(int, int, int) method in PrimeFinder using a WorkQueue. There should be 1 task or Runnable object for each number being tested, and the run() method of these objects should use the isPrime(int) method.
Requirements
The official name of this homework is PrimeFinder. This should be the name you use for your Eclipse Java project and the name you use when running the homework test script.
See the Homework Guides for additional details on homework requirements and submission.
关键类实现
PrimeFinder
import java.util.TreeSet;
/**
* Finds primes, with an inefficient single-threaded implementation made somewhat
* less inefficient with multithreading using a work queue.
*
* @author CS 212 Software Development
* @author University of San Francisco
* @version Spring 2021
*/
public class PrimeFinder {
/**
* A terrible and naive approach to determining if a number is prime.
*
* @param number to test if prime
* @return true if the number is prime
*/
public static boolean isPrime(int number) {
if (number < 2) {
return false;
}
for (int i = number - 1; i > 1; i--) {
if (number % i == 0) {
return false;
}
}
return true;
}
/*
* This is an intentionally TERRIBLE implementation to cause a long-running
* calculation. There really is no realistic use of this approach.
*/
/**
* Returns a collection of all primes less than or equal to the max value.
*
* @param max the maximum value to evaluate if prime
* @return all prime numbers found up to and including max
*/
public static TreeSet<Integer> trialDivision(int max) {
TreeSet<Integer> primes = new TreeSet<Integer>();
if (max > 0) {
for (int i = 1; i <= max; i++) {
if (isPrime(i)) {
primes.add(i);
}
}
}
return primes;
}
/**
* Uses a work queue to find all primes less than or equal to the maximum
* value. The number of threads must be a positive number greater than or
* equal to 1.
*
* @param max the maximum value to evaluate if prime
* @param threads number of worker threads (must be positive)
* @return all prime numbers found up to and including max
*/
public static TreeSet<Integer> findPrimes(int max, int threads) {
// TODO Fix this implementation (see trialDivision for starting point).
TreeSet<Integer> primes = new TreeSet<Integer>();
WorkQueue queue = new WorkQueue(threads);
if (max > 0) {
for (int i = 1; i <= max; i++) {
queue.execute(new Task(i, primes));
}
}
queue.join();
return primes;
}
// TODO Add additional classes or methods as needed.
public static class Task implements Runnable {
private final int number;
private final TreeSet<Integer> result;
public Task(int number, TreeSet<Integer> result) {
this.number = number;
this.result = result;
}
@Override
public void run() {
if (isPrime(number)) {
synchronized (result) {
result.add(number);
}
}
}
}
/**
* Demonstrates this class.
*
* @param args unused
*/
public static void main(String[] args) {
int max = 100;
int threads = 3;
// see if the single and mutli threaded versions return the same result
System.out.println("Comparing prime numbers:");
System.out.println(trialDivision(max));
System.out.println(findPrimes(max, threads));
System.out.println();
}
}
WorkQueue
import java.util.Arrays;
import java.util.LinkedList;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* A simple work queue implementation based on the IBM developerWorks article by
* Brian Goetz. It is up to the user of this class to keep track of whether
* there is any pending work remaining.
*
* @see <a href="https://www.ibm.com/developerworks/library/j-jtp0730/">
* Java Theory and Practice: Thread Pools and Work Queues</a>
*
* @author CS 212 Software Development
* @author University of San Francisco
* @version Spring 2021
*/
public class WorkQueue {
// TODO Modify existing methods if necessary.
// TODO Add new members and methods if necessary.
public static int taskNum;
private synchronized void addTask() {
taskNum ++ ;
}
private synchronized void deleteTask() {
taskNum -- ;
}
/**
* Pool of worker threads that will wait in the background until work is
* available.
*/
private final Worker[] workers;
/** Queue of pending work requests. */
private final LinkedList<Runnable> queue;
/** Used to signal the queue should be shutdown. */
private volatile boolean shutdown;
/** The default number of threads to use when not specified. */
public static final int DEFAULT = 5;
/** Logger used for this class. */
private static final Logger log = LogManager.getLogger();
/**
* Starts a work queue with the default number of threads.
*
* @see #WorkQueue(int)
*/
public WorkQueue() {
this(DEFAULT);
}
/**
* Starts a work queue with the specified number of threads.
*
* @param threads number of worker threads; should be greater than 1
*/
public WorkQueue(int threads) {
this.queue = new LinkedList<Runnable>();
this.workers = new Worker[threads];
shutdown = false;
// start the threads so they are waiting in the background
for (int i = 0; i < threads; i++) {
workers[i] = new Worker();
workers[i].start();
}
log.debug("Work queue initialized with {} worker threads.", workers.length);
}
/**
* Adds a work request to the queue. A thread will process this request when
* available.
*
* @param task work request (in the form of a {@link Runnable} object)
*/
public void execute(Runnable task) {
synchronized (queue) {
queue.addLast(task);
addTask();
queue.notifyAll();
}
}
/**
* Waits for all pending work to be finished. Does not terminate the worker
* threads so that the work queue can continue to be used.
*/
public void finish() {
// TODO Fix this method implementation.
synchronized (queue) {
while (taskNum > 0) {
try {
queue.wait();
} catch (InterruptedException e) {
System.err.println("Interrupted Exception!");
}
}
}
}
/**
* Asks the queue to shutdown. Any unprocessed work will not be finished, but
* threads in-progress will not be interrupted.
*/
public void shutdown() {
// safe to do unsynchronized due to volatile keyword
shutdown = true;
log.debug("Work queue triggering shutdown...");
synchronized (queue) {
queue.notifyAll();
}
}
/**
* Similar to {@link Thread#join()}, waits for all the work to be finished
* and the worker threads to terminate. The work queue cannot be reused after
* this call completes.
*/
public void join() {
finish();
shutdown();
for (Worker worker : workers) {
try {
worker.join();
}
catch (InterruptedException e) {
System.err.println("Warning: Work queue interrupted while joining.");
log.catching(Level.DEBUG, e);
Thread.currentThread().interrupt();
}
}
log.debug("All worker threads terminated.");
}
/**
* Returns the number of worker threads being used by the work queue.
*
* @return number of worker threads
*/
public int size() {
return workers.length;
}
/**
* Waits until work is available in the work queue. When work is found, will
* remove the work from the queue and run it. If a shutdown is detected, will
* exit instead of grabbing new work from the queue. These threads will
* continue running in the background until a shutdown is requested.
*/
private class Worker extends Thread {
/**
* Initializes a worker thread with a custom name.
*/
public Worker() {
setName("Worker" + getName());
}
@Override
public void run() {
Runnable task = null;
while (true) {
synchronized (queue) {
while (queue.isEmpty() && !shutdown) {
try {
log.debug("Work queue worker waiting...");
queue.wait();
}
catch (InterruptedException e) {
System.err.println("Warning: Work queue interrupted while waiting.");
log.catching(Level.DEBUG, e);
Thread.currentThread().interrupt();
}
}
// exit while for one of two reasons:
// (a) queue has work, or (b) shutdown has been called
if (shutdown) {
log.debug("Work queue worker shutting down...");
break;
}
else {
task = queue.removeFirst();
}
}
deleteTask();
synchronized (queue) {
if (taskNum <= 0) {
queue.notifyAll();
}
}
try {
log.debug("Work queue worker found work.");
task.run();
}
catch (RuntimeException e) {
// catch runtime exceptions to avoid leaking threads
System.err.println("Warning: Work queue encountered an exception while running.");
log.catching(Level.DEBUG, e);
}
}
}
}
// TODO Remove main method when done testing!
/**
* Demonstrates this class.
*
* @param args unused
*/
public static void main(String[] args) {
int threads = 3;
Supplier<String> activeThreads = () -> {
Thread[] found = new Thread[Thread.activeCount() * 2];
Thread.enumerate(found);
return Arrays.stream(found).filter(t -> t != null).map(Thread::getName).collect(Collectors.joining(", "));
};
// demonstrates the workers in the background
WorkQueue demo = new WorkQueue(threads);
// do a bit of work in the background
demo.execute(() -> {
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// finish, but do not shutdown
demo.finish();
// peek at the threads active in the system
System.out.println("Estimated active threads before join(): " + activeThreads.get());
// trigger threads to shutdown
demo.join();
// peek at the threads after shutdown and join
System.out.println("Estimated active threads after join(): " + activeThreads.get());
/*
* The thread named "main" runs the main method.
*
* If you see any thread names starting with "ForkJoinPool", those
* are used internally. They could come from Log4j2 or JUnit.
*
* If you see any thread names starting with "WorkerThread", those
* are likely your work queue threads.
*
* If you see your work queue threads AFTER the join() call, something
* is not quite working with the finish and/or shutdown calls.
*/
}
}
源码地址
https://download.csdn.net/download/rainbowBear/16730962