diff --git a/BuggyQueue/.classpath b/BuggyQueue/.classpath deleted file mode 100644 index 18d70f02c..000000000 --- a/BuggyQueue/.classpath +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/BuggyQueue/.project b/BuggyQueue/.project deleted file mode 100644 index 98aa6b7c1..000000000 --- a/BuggyQueue/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - BuggyQueue - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/BuggyQueue/build.gradle b/BuggyQueue/build.gradle new file mode 100644 index 000000000..d88d747f6 --- /dev/null +++ b/BuggyQueue/build.gradle @@ -0,0 +1,15 @@ +group 'livelessons' +version '1.0-SNAPSHOT' + +apply plugin: 'java' + +targetCompatibility = 1.8 +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.11' +} diff --git a/BuggyQueue/gradle/wrapper/gradle-wrapper.jar b/BuggyQueue/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..ca78035ef Binary files /dev/null and b/BuggyQueue/gradle/wrapper/gradle-wrapper.jar differ diff --git a/BuggyQueue/gradle/wrapper/gradle-wrapper.properties b/BuggyQueue/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..be3fb022c --- /dev/null +++ b/BuggyQueue/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Aug 16 18:00:52 EDT 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip diff --git a/BuggyQueue/gradlew b/BuggyQueue/gradlew new file mode 100644 index 000000000..27309d923 --- /dev/null +++ b/BuggyQueue/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/BuggyQueue/gradlew.bat b/BuggyQueue/gradlew.bat new file mode 100644 index 000000000..832fdb607 --- /dev/null +++ b/BuggyQueue/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/BuggyQueue/settings.gradle b/BuggyQueue/settings.gradle new file mode 100644 index 000000000..96c454ba3 --- /dev/null +++ b/BuggyQueue/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'BuggyQueue' + diff --git a/BuggyQueue/src/BuggyQueueTest.java b/BuggyQueue/src/BuggyQueueTest.java deleted file mode 100644 index bb12349d7..000000000 --- a/BuggyQueue/src/BuggyQueueTest.java +++ /dev/null @@ -1,147 +0,0 @@ -import java.util.concurrent.*; -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.List; - -/** - * @class BuggyQueueTest - * - * @brief Test program for the SimpleQueue that induces race - * conditions due to lack of synchronization. - */ -public class BuggyQueueTest -{ - /** - * Maximum number of iterations. - */ - private final static int mMaxIterations = 1000000; - - /** - * Maximum size of the queue. - */ - private final static int mQueueSize = 10; - - /** - * Count the number of iterations. - */ - private final static AtomicInteger mCount = - new AtomicInteger(0); - - /** - * @class ProducerThread - * - * @brief This producer runs in a separate Java Thread and passes - * Strings to a consumer Thread via a shared BlockingQueue. - */ - static class ProducerThread extends Thread { - /** - * This queue is shared with the consumer. - */ - private final BQ mQueue; - - /** - * Constructor initializes the BlockingQueue data - * member. - */ - ProducerThread(BQ blockingQueue) { - mQueue = blockingQueue; - } - - /** - * This method runs in a separate Java Thread and passes - * Strings to a consumer Thread via a shared BlockingQueue. - */ - public void run(){ - try { - for(int i = 0; i < mMaxIterations; i++) { - mCount.incrementAndGet(); - - // Calls the put() method. - mQueue.put(Integer.toString(i)); - } - } catch (InterruptedException e) { - System.out.println("InterruptedException caught"); - } - } - } - - /** - * @class ConsumerThread - * - * @brief This consumer runs in a separate Java Thread and - * receives Strings from a producer Thread via a shared - * BlockingQueue. - */ - static class ConsumerThread extends Thread { - /** - * This queue is shared with the producer. - */ - private final BQ mQueue; - - /** - * Constructor initializes the BlockingQueue data member. - */ - ConsumerThread(BQ blockingQueue) { - mQueue = blockingQueue; - } - - /** - * This method runs in a separate Java Thread and receives - * Strings from a producer Thread via a shared BlockingQueue. - */ - public void run(){ - Object s = null; - try { - for(int i = 0; i < mMaxIterations; i++) { - // Calls the take() method. - s = mQueue.take(); - - mCount.decrementAndGet(); - - if((i % (mMaxIterations / 10)) == 0) - System.out.println(s == null ? "" : s); - } - } catch (InterruptedException e) { - System.out.println("InterruptedException caught"); - } - System.out.println("Final size of the queue is " - + mQueue.size() - + "\nmCount is " - + mCount.get() - + "\nFinal value is " - + s); - } - } - - /** - * Main entry point that tests the SimpleQueue class. - */ - public static void main(String argv[]) { - final SimpleQueue simpleQueue = - new SimpleQueue(); // (mQueueSize); - - try { - // Create a ProducerThread. - Thread producer = - new ProducerThread(simpleQueue); - - // Create a ConsumerThread. - Thread consumer = - new ConsumerThread(simpleQueue); - - // Run both Threads concurrently. - producer.start(); - try { - Thread.sleep(100); - } catch (InterruptedException e) {} - - consumer.start(); - - // Wait for both Threads to stop. - producer.join(); - consumer.join(); - } catch (Exception e) { - System.out.println("caught exception"); - } - } -} diff --git a/BuggyQueue/src/SimpleQueue.java b/BuggyQueue/src/SimpleQueue.java deleted file mode 100644 index fea73deab..000000000 --- a/BuggyQueue/src/SimpleQueue.java +++ /dev/null @@ -1,163 +0,0 @@ -import java.util.List; -import java.util.concurrent.CyclicBarrier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.TimeUnit; - -/* - * @class SimpleQueue - * - * @brief Defines an implementation of the BlockingQueue interface - * that (intentially) doesn't work properly when accessed via - * multiple threads since it's not synchronized properly. - */ -class SimpleQueue implements BlockingQueue { - /** - * The queue consists of a List of E's. - */ - private List mList = new ArrayList(); - - /** - * The maximum capacity of the queue or Integer.MAX_VALUE if none. - */ - private final int mCapacity; - - /** - * Create a SimpleBlocking queue with a capacity of - * Integer.MAX_VALUE. - */ - public SimpleQueue() { - this(Integer.MAX_VALUE); - } - - /** - * Create a SimpleBlocking queue with the given capacity. - */ - public SimpleQueue(int capacity) { - if (capacity <= 0) - throw new IllegalArgumentException(); - mCapacity = capacity; - mList = new ArrayList(); - } - - /** - * True if the queue is empty. - */ - public boolean isEmpty() { - return mList.size() == 0; - } - - /** - * Returns true if the queue is full, else false. - */ - private boolean isFull() { - return mList.size() == mCapacity; - } - - /** - * Add a new E to the end of the queue. - */ - public void put(E msg) throws InterruptedException { - if (isFull() == false) - mList.add(msg); - } - - /** - * Remove the E at the front of the queue. - */ - public E take() throws InterruptedException { - if (isEmpty() == false) - return mList.remove(0); - else - return null; - } - - /** - * Returns the number of elements in this queue. - */ - public int size() { - return mList.size(); - } - - /** - * All these methods are inherited from the BlockingQueue - * interface. They are defined as no-ops to ensure the "Buggyness" - * of this class ;-) - */ - public int drainTo(Collection c) { - return 0; - } - public int drainTo(Collection c, int maxElements) { - return 0; - } - public boolean contains(Object o) { - return false; - } - public boolean remove(Object o) { - return false; - } - public int remainingCapacity() { - return 0; - } - public E poll() { - return null; - } - public E poll(long timeout, TimeUnit unit) throws InterruptedException { - return take(); - } - public E peek() { - return null; - } - public boolean offer(E e) { - return false; - } - public boolean offer(E e, long timeout, TimeUnit unit) { - try { - put(e); - } - catch (InterruptedException ex) { - // Just swallow this exception for this simple (buggy) test. - } - return true; - } - public boolean add(E e) { - return false; - } - public E element() { - return null; - } - public E remove() { - return null; - } - public void clear() { - } - public boolean retainAll(Collection collection) { - return false; - } - public boolean removeAll(Collection collection) { - return false; - } - public boolean addAll(Collection collection) { - return false; - } - public boolean containsAll(Collection collection) { - return false; - } - public Object[] toArray() { - return null; - } - public T[] toArray(T[] array) { - return null; - } - public Iterator iterator() { - return null; - } -} - - - - - - diff --git a/BuggyQueue/src/main/java/edu/vandy/BoundedQueue.java b/BuggyQueue/src/main/java/edu/vandy/BoundedQueue.java new file mode 100644 index 000000000..c2eda31e3 --- /dev/null +++ b/BuggyQueue/src/main/java/edu/vandy/BoundedQueue.java @@ -0,0 +1,73 @@ +package edu.vandy; + +/** + * Defines an interface for a bounded queue. + */ +public interface BoundedQueue { + /** + * Inserts the specified element into this queue, waiting if + * necessary for space to become available. + * + * @param e the element to add + * @throws InterruptedException if interrupted while waiting + */ + default void put(E e) + throws InterruptedException { + } + + /** + * Retrieves and removes the head of this queue, waiting if necessary + * until an element becomes available. + * + * @return the head of this queue + * @throws InterruptedException if interrupted while waiting + */ + default E take() + throws InterruptedException { + return null; + } + + /** + * Retrieves and removes the head of this queue, or returns {@code + * null} if this queue is empty. + * + * @return the head of this queue, or {@code null} if this queue is empty + */ + default E poll() { + return null; + } + + /** + * Inserts the specified element into this queue if it is possible to do + * so immediately without violating capacity restrictions, returning + * {@code true} upon success and {@code false} if no space is currently + * available. + * + * @return {@code true} if the element was added to this queue, else + * {@code false} + */ + default boolean offer(E e) { + return false; + } + + /** + * Returns true if this queue contains no elements, else false. + * + * @return true if this queue contains no elements, else false. + */ + boolean isEmpty(); + + /** + * Returns true if this queue is full, else false. + * + * @return true if this queue is full, else false. + */ + boolean isFull(); + + /** + * Returns the number of elements in this queue. + * + * @return the number of elements in this collection + */ + int size(); +} diff --git a/BuggyQueue/src/main/java/edu/vandy/BuggyQueue.java b/BuggyQueue/src/main/java/edu/vandy/BuggyQueue.java new file mode 100644 index 000000000..b9d8058c6 --- /dev/null +++ b/BuggyQueue/src/main/java/edu/vandy/BuggyQueue.java @@ -0,0 +1,106 @@ +package edu.vandy; + +import java.util.List; +import java.util.concurrent.CyclicBarrier; +import java.util.LinkedList; +import java.util.Collection; +import java.util.Iterator; +import java.util.concurrent.TimeUnit; + +/* + * Defines an implementation of the BoundedQueue interface that + * (intentially) doesn't work properly when accessed via multiple + * threads since it's not synchronized properly. + */ +class BuggyQueue + implements BoundedQueue { + /** + * The queue consists of a LinkedList of E's. + */ + private List mList = new LinkedList<>(); + + /** + * The maximum capacity of the queue or Integer.MAX_VALUE if none. + */ + private final int mCapacity; + + /** + * Create a BuggyQueue with a capacity of Integer.MAX_VALUE. + */ + public BuggyQueue() { + this(Integer.MAX_VALUE); + } + + /** + * Create a BuggyQueue with the given capacity. + */ + public BuggyQueue(int capacity) { + if (capacity <= 0) + throw new IllegalArgumentException(); + mCapacity = capacity; + mList = new LinkedList<>(); + } + + /** + * Retrieves and removes the head of this queue, or returns {@code + * null} if this queue is empty. + * + * @return the head of this queue, or {@code null} if this queue is empty + */ + public E poll() { + if (!isEmpty()) + return mList.remove(0); + else + return null; + } + + /** + * Inserts the specified element into this queue if it is possible to do + * so immediately without violating capacity restrictions, returning + * {@code true} upon success and {@code false} if no space is currently + * available. + * + * @return {@code true} if the element was added to this queue, else + * {@code false} + */ + public boolean offer(E e) { + if (!isFull()) { + mList.add(e); + return true; + } else + return false; + } + + /** + * Returns true if this queue contains no elements, else false. + * + * @return true if this queue contains no elements, else false. + */ + public boolean isEmpty() { + return mList.size() == 0; + } + + /** + * Returns true if this queue is full, else false. + * + * @return true if this queue is full, else false. + */ + public boolean isFull() { + return mList.size() == mCapacity; + } + + /** + * Returns the number of elements in this queue. + * + * @return the number of elements in this collection + */ + public int size() { + return mList.size(); + } +} + + + + + + diff --git a/BuggyQueue/src/test/java/edu/vandy/BuggyQueueTest.java b/BuggyQueue/src/test/java/edu/vandy/BuggyQueueTest.java new file mode 100644 index 000000000..2788c78a4 --- /dev/null +++ b/BuggyQueue/src/test/java/edu/vandy/BuggyQueueTest.java @@ -0,0 +1,169 @@ +package edu.vandy; + +import static org.junit.Assert.assertEquals; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +/** + * Test program for the BuggyQueue that induces race conditions due to + * lack of synchronization. + */ +public class BuggyQueueTest { + /** + * Maximum number of iterations. + */ + private final static int mMaxIterations = 100000; + + /** + * Maximum size of the queue. + */ + private final static int sQUEUE_SIZE = 10; + + /** + * Count the number of iterations. + */ + private final static AtomicInteger mCount = + new AtomicInteger(0); + + /** + * This producer runs in a separate Java thread and passes strings + * to a consumer thread via a shared BoundedQueue. + */ + private static class Producer> + implements Runnable { + /** + * This queue is shared with the consumer. + */ + private final BQ mQueue; + + /** + * Constructor initializes the BoundedQueue data member. + */ + Producer(BQ blockingQueue) { + mQueue = blockingQueue; + } + + /** + * This method runs in a separate Java thread and passes + * strings to a consumer thread via a shared BoundedQueue. + */ + public void run() { + for (int i = 0; i < mMaxIterations; ) { + // Calls the offer() method. + if (mQueue.offer(i)) { + i++; + mCount.incrementAndGet(); + } else + Thread.yield(); + } + } + } + + /** + * This consumer runs in a separate Java thread and receives + * strings from a producer thread via a shared BoundedQueue. + */ + private static class Consumer> + implements Runnable { + /** + * This queue is shared with the producer. + */ + private final BQ mQueue; + + /** + * Constructor initializes the BoundedQueue data member. + */ + Consumer(BQ blockingQueue) { + mQueue = blockingQueue; + } + + /** + * This method runs in a separate Java thread and receives + * strings from a producer thread via a shared BoundedQueue. + */ + public void run() { + Integer integer = null; + int nullCount = 0; + + // Get the first item from the queue. + Integer previous = null; + + // Get the first non-null value. + while ((previous = mQueue.poll()) == null) + continue; + + mCount.decrementAndGet(); + + for (int i = 1; i < mMaxIterations; ) { + // Try to get the next integer. + integer = mQueue.poll(); + + // Only update the state if we get a non-null + // value from take(). + if (integer != null) { + // Make sure the entries are ordered. + assertEquals(previous + 1, integer.intValue()); + previous = integer; + + if ((i % (mMaxIterations / 10)) == 0) + System.out.println(integer); + mCount.decrementAndGet(); + i++; + + } else { + nullCount++; + Thread.yield(); + } + } + + assertEquals(0, mCount.get()); + + System.out.println("Final size of the queue is " + + mQueue.size() + + "\nmCount is " + + mCount.get() + + "\nFinal value is " + + integer + + "\nnumber of null returns from take() is " + + nullCount + + "\nmCount + nullCount is " + + (mCount.get() + nullCount)); + } + } + + /** + * Main entry point that tests the SimpleQueue class. + */ + @Test(timeout=10000) + public void testBuggyQueue() { + final BuggyQueue buggyQueue = + new BuggyQueue<>(sQUEUE_SIZE); + + try { + // Create producer and consumer threads. + Thread[] threads = new Thread[] { + new Thread(new Producer<>(buggyQueue)), + new Thread(new Consumer<>(buggyQueue)) + }; + + // Record the start time. + long startTime = System.nanoTime(); + + // Start all the threads. + for (Thread thread : threads) + thread.start(); + + // Wait for all threads to stop. + for (Thread thread : threads) + thread.join(); + + System.out.println("test ran in " + + (System.nanoTime() - startTime) / 1_000_000 + + " msecs"); + } catch (Exception e) { + System.out.println("caught exception"); + } + } +} diff --git a/BusySynchronizedQueue/.classpath b/BusySynchronizedQueue/.classpath deleted file mode 100644 index 18d70f02c..000000000 --- a/BusySynchronizedQueue/.classpath +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/BusySynchronizedQueue/.project b/BusySynchronizedQueue/.project deleted file mode 100644 index 40c74dd7f..000000000 --- a/BusySynchronizedQueue/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - BusySynchronizedQueue - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/BusySynchronizedQueue/build.gradle b/BusySynchronizedQueue/build.gradle new file mode 100644 index 000000000..d88d747f6 --- /dev/null +++ b/BusySynchronizedQueue/build.gradle @@ -0,0 +1,15 @@ +group 'livelessons' +version '1.0-SNAPSHOT' + +apply plugin: 'java' + +targetCompatibility = 1.8 +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.11' +} diff --git a/BusySynchronizedQueue/gradle/wrapper/gradle-wrapper.jar b/BusySynchronizedQueue/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..ca78035ef Binary files /dev/null and b/BusySynchronizedQueue/gradle/wrapper/gradle-wrapper.jar differ diff --git a/BusySynchronizedQueue/gradle/wrapper/gradle-wrapper.properties b/BusySynchronizedQueue/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..be3fb022c --- /dev/null +++ b/BusySynchronizedQueue/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Aug 16 18:00:52 EDT 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip diff --git a/BusySynchronizedQueue/gradlew b/BusySynchronizedQueue/gradlew new file mode 100644 index 000000000..27309d923 --- /dev/null +++ b/BusySynchronizedQueue/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/BusySynchronizedQueue/gradlew.bat b/BusySynchronizedQueue/gradlew.bat new file mode 100644 index 000000000..832fdb607 --- /dev/null +++ b/BusySynchronizedQueue/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/BusySynchronizedQueue/settings.gradle b/BusySynchronizedQueue/settings.gradle new file mode 100644 index 000000000..2768ef94f --- /dev/null +++ b/BusySynchronizedQueue/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'BusySynchronizedQueue' + diff --git a/BusySynchronizedQueue/src/BusySynchronizedQueue.java b/BusySynchronizedQueue/src/BusySynchronizedQueue.java deleted file mode 100644 index a245c9dcf..000000000 --- a/BusySynchronizedQueue/src/BusySynchronizedQueue.java +++ /dev/null @@ -1,163 +0,0 @@ -import java.util.List; -import java.util.concurrent.CyclicBarrier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.TimeUnit; - -/* - * @class BusySynchronizedQueue - * - * @brief Defines an implementation of the BlockingQueue interface - * that works properly when accessed via multiple threads since - * it's synchronized properly, but is inefficient since it - * "busy waits". - */ -class BusySynchronizedQueue implements BlockingQueue { - /** - * The queue consists of a List of E's. - */ - private List mList = new ArrayList(); - - /** - * The maximum capacity of the queue or Integer.MAX_VALUE if none. - */ - private final int mCapacity; - - /** - * Create a SimpleBlocking queue with a capacity of - * Integer.MAX_VALUE. - */ - public BusySynchronizedQueue() { - this(Integer.MAX_VALUE); - } - - /** - * Create a SimpleBlocking queue with the given capacity. - */ - public BusySynchronizedQueue(int capacity) { - if (capacity <= 0) - throw new IllegalArgumentException(); - mCapacity = capacity; - mList = new ArrayList(); - } - - /** - * True if the queue is empty. - */ - public synchronized boolean isEmpty() { - return mList.size() == 0; - } - - /** - * Returns true if the queue is full, else false. - */ - private synchronized boolean isFull() { - return mList.size() == mCapacity; - } - - /** - * Add a new E to the end of the queue. - */ - public synchronized void put(E msg) throws InterruptedException { - if (isFull() == false) - mList.add(msg); - } - - /** - * Remove the E at the front of the queue. - */ - public synchronized E take() throws InterruptedException { - if (isEmpty() == false) - return mList.remove(0); - else - return null; - } - - /** - * Returns the number of elements in this queue. - */ - public synchronized int size() { - return mList.size(); - } - - /** - * All these methods are inherited from the BlockingQueue - * interface. All are defined as no-ops for simplicity. - */ - public int drainTo(Collection c) { - return 0; - } - public int drainTo(Collection c, int maxElements) { - return 0; - } - public boolean contains(Object o) { - return false; - } - public boolean remove(Object o) { - return false; - } - public int remainingCapacity() { - return 0; - } - public E poll() { - return null; - } - public E poll(long timeout, TimeUnit unit) throws InterruptedException { - return take(); - } - public E peek() { - return null; - } - public boolean offer(E e) { - return false; - } - public boolean offer(E e, long timeout, TimeUnit unit) { - try { - put(e); - } - catch (InterruptedException ex) { - // Just swallow this exception for this simple (buggy) test. - } - return true; - } - public boolean add(E e) { - return false; - } - public E element() { - return null; - } - public E remove() { - return null; - } - public void clear() { - } - public boolean retainAll(Collection collection) { - return false; - } - public boolean removeAll(Collection collection) { - return false; - } - public boolean addAll(Collection collection) { - return false; - } - public boolean containsAll(Collection collection) { - return false; - } - public Object[] toArray() { - return null; - } - public T[] toArray(T[] array) { - return null; - } - public Iterator iterator() { - return null; - } -} - - - - - - diff --git a/BusySynchronizedQueue/src/BusySynchronizedQueueTest.java b/BusySynchronizedQueue/src/BusySynchronizedQueueTest.java deleted file mode 100644 index 0248cc503..000000000 --- a/BusySynchronizedQueue/src/BusySynchronizedQueueTest.java +++ /dev/null @@ -1,148 +0,0 @@ -import java.util.concurrent.*; -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.List; - -/** - * @class BusySynchronizedQueueTest - * - * @brief Test program for the BusySynchronizedQueue that is - * inefficient due to "busy waiting". - */ -public class BusySynchronizedQueueTest -{ - /** - * Maximum number of iterations. - */ - private final static int mMaxIterations = 1000000; - - /** - * Maximum size of the queue. - */ - private final static int mQueueSize = 10; - - /** - * Count the number of iterations. - */ - private final static AtomicInteger mCount = - new AtomicInteger(0); - - /** - * @class ProducerThread - * - * @brief This producer runs in a separate Java Thread and passes - * Strings to a consumer Thread via a shared BlockingQueue. - */ - static class ProducerThread extends Thread { - /** - * This queue is shared with the consumer. - */ - private final BQ mQueue; - - /** - * Constructor initializes the BlockingQueue data - * member. - */ - ProducerThread(BQ blockingQueue) { - mQueue = blockingQueue; - } - - /** - * This method runs in a separate Java Thread and passes - * Strings to a consumer Thread via a shared BlockingQueue. - */ - public void run(){ - try { - for(int i = 0; i < mMaxIterations; i++) { - mCount.incrementAndGet(); - - // Calls the put() method. - mQueue.put(Integer.toString(i)); - } - } catch (InterruptedException e) { - System.out.println("InterruptedException caught"); - } - } - } - - /** - * @class ConsumerThread - * - * @brief This consumer runs in a separate Java Thread and - * receives Strings from a producer Thread via a shared - * BlockingQueue. - */ - static class ConsumerThread extends Thread { - /** - * This queue is shared with the producer. - */ - private final BQ mQueue; - - /** - * Constructor initializes the BlockingQueue data member. - */ - ConsumerThread(BQ blockingQueue) { - mQueue = blockingQueue; - } - - /** - * This method runs in a separate Java Thread and receives - * Strings from a producer Thread via a shared BlockingQueue. - */ - public void run(){ - Object s = null; - try { - for(int i = 0; i < mMaxIterations; i++) { - // Calls the take() method. - s = mQueue.take(); - - mCount.decrementAndGet(); - - if ((i % (mMaxIterations / 10)) == 0) - System.out.println(s == null ? "" : s); - } - } catch (InterruptedException e) { - System.out.println("InterruptedException caught"); - } - System.out.println("Final size of the queue is " - + mQueue.size() - + "\nmCount is " - + mCount.get() - + "\nFinal value is " - + s); - } - } - - /** - * Main entry point that tests the BusySynchronizedQueue class. - */ - public static void main(String argv[]) { - final BusySynchronizedQueue busySynchronizedQueue = - new BusySynchronizedQueue(); // (mQueueSize); - - try { - // Create a ProducerThread. - Thread producer = - new ProducerThread(busySynchronizedQueue); - - // Create a ConsumerThread. - Thread consumer = - new ConsumerThread(busySynchronizedQueue); - - // Run both Threads concurrently. - producer.start(); - - try { - Thread.sleep(100); - } catch (InterruptedException e) {} - - consumer.start(); - - // Wait for both Threads to stop. - producer.join(); - consumer.join(); - } catch (Exception e) { - System.out.println("caught exception"); - } - } -} diff --git a/BusySynchronizedQueue/src/main/java/edu/vandy/BoundedQueue.java b/BusySynchronizedQueue/src/main/java/edu/vandy/BoundedQueue.java new file mode 100644 index 000000000..c2eda31e3 --- /dev/null +++ b/BusySynchronizedQueue/src/main/java/edu/vandy/BoundedQueue.java @@ -0,0 +1,73 @@ +package edu.vandy; + +/** + * Defines an interface for a bounded queue. + */ +public interface BoundedQueue { + /** + * Inserts the specified element into this queue, waiting if + * necessary for space to become available. + * + * @param e the element to add + * @throws InterruptedException if interrupted while waiting + */ + default void put(E e) + throws InterruptedException { + } + + /** + * Retrieves and removes the head of this queue, waiting if necessary + * until an element becomes available. + * + * @return the head of this queue + * @throws InterruptedException if interrupted while waiting + */ + default E take() + throws InterruptedException { + return null; + } + + /** + * Retrieves and removes the head of this queue, or returns {@code + * null} if this queue is empty. + * + * @return the head of this queue, or {@code null} if this queue is empty + */ + default E poll() { + return null; + } + + /** + * Inserts the specified element into this queue if it is possible to do + * so immediately without violating capacity restrictions, returning + * {@code true} upon success and {@code false} if no space is currently + * available. + * + * @return {@code true} if the element was added to this queue, else + * {@code false} + */ + default boolean offer(E e) { + return false; + } + + /** + * Returns true if this queue contains no elements, else false. + * + * @return true if this queue contains no elements, else false. + */ + boolean isEmpty(); + + /** + * Returns true if this queue is full, else false. + * + * @return true if this queue is full, else false. + */ + boolean isFull(); + + /** + * Returns the number of elements in this queue. + * + * @return the number of elements in this collection + */ + int size(); +} diff --git a/BusySynchronizedQueue/src/main/java/edu/vandy/BusySynchronizedQueue.java b/BusySynchronizedQueue/src/main/java/edu/vandy/BusySynchronizedQueue.java new file mode 100644 index 000000000..3a8909441 --- /dev/null +++ b/BusySynchronizedQueue/src/main/java/edu/vandy/BusySynchronizedQueue.java @@ -0,0 +1,121 @@ +package edu.vandy; + +import java.util.List; +import java.util.LinkedList; +import java.util.Collection; +import java.util.Iterator; +import java.util.concurrent.TimeUnit; + +/* + * Implements the BoundedQueue interface that works properly when + * accessed via multiple threads since it's synchronized properly, but + * is inefficient since due to its "busy waiting". + */ +class BusySynchronizedQueue + implements BoundedQueue { + /** + * The queue consists of a LinkedList of E's. + */ + private List mList = new LinkedList<>(); + + /** + * The maximum capacity of the queue or Integer.MAX_VALUE if none. + */ + private final int mCapacity; + + /** + * Create a SimpleBlocking queue with a capacity of + * Integer.MAX_VALUE. + */ + public BusySynchronizedQueue() { + this(Integer.MAX_VALUE); + } + + /** + * Create a SimpleBlocking queue with the given capacity. + */ + public BusySynchronizedQueue(int capacity) { + if (capacity <= 0) + throw new IllegalArgumentException(); + mCapacity = capacity; + mList = new LinkedList<>(); + } + + /** + * Returns true if this queue contains no elements, else false. + * + * @return true if this queue contains no elements, else false. + */ + @Override + public boolean isEmpty() { + synchronized(this) { + return mList.size() == 0; + } + } + + /** + * Returns true if this queue is full, else false. + * + * @return true if this queue is full, else false. + */ + @Override + public boolean isFull() { + synchronized(this) { + return mList.size() == mCapacity; + } + } + + /** + * Returns the number of elements in this queue. + * + * @return the number of elements in this collection + */ + @Override + public int size() { + synchronized(this) { + return mList.size(); + } + } + + /** + * Retrieves and removes the head of this queue, or returns {@code + * null} if this queue is empty. + * + * @return the head of this queue, or {@code null} if this queue is empty + */ + @Override + public E poll() { + synchronized(this) { + if (!isEmpty()) + return mList.remove(0); + else + return null; + } + } + + /** + * Inserts the specified element into this queue if it is possible to do + * so immediately without violating capacity restrictions, returning + * {@code true} upon success and {@code false} if no space is currently + * available. + * + * @return {@code true} if the element was added to this queue, else + * {@code false} + */ + @Override + public boolean offer(E e) { + synchronized (this) { + if (!isFull()) { + mList.add(e); + return true; + } else + return false; + } + } +} + + + + + + diff --git a/BusySynchronizedQueue/src/test/java/edu/vandy/BusySynchronizedQueueTest.java b/BusySynchronizedQueue/src/test/java/edu/vandy/BusySynchronizedQueueTest.java new file mode 100644 index 000000000..2bfb3931f --- /dev/null +++ b/BusySynchronizedQueue/src/test/java/edu/vandy/BusySynchronizedQueueTest.java @@ -0,0 +1,170 @@ +package edu.vandy; + +import static org.junit.Assert.assertEquals; + +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +/** + * Test program for the BusySynchronizedQueue that is inefficient due + * to its use of "busy waiting". + */ +public class BusySynchronizedQueueTest { + /** + * Maximum number of iterations. + */ + private final static int mMaxIterations = 100000; + + /** + * Maximum size of the queue. + */ + private final static int sQUEUE_SIZE = 10; + + /** + * Count the number of iterations. + */ + private final static AtomicInteger mCount = + new AtomicInteger(0); + + /** + * This producer runs in a separate Java thread and passes strings + * to a consumer thread via a shared BoundedBlockingQueue. + */ + private static class Producer> + implements Runnable { + /** + * This queue is shared with the consumer. + */ + private final BQ mQueue; + + /** + * Constructor initializes the @a blockingQueue field. + */ + Producer(BQ blockingQueue) { + mQueue = blockingQueue; + } + + /** + * This method runs in a Java thread and passes strings to a + * consumer thread via a shared BoundedBlockingQueue. + */ + public void run() { + for(int i = 0; i < mMaxIterations; ) { + // Calls the offer() method. + if (mQueue.offer(i)) { + i++; + mCount.incrementAndGet(); + } + } + } + } + + /** + * This consumer runs in a Java thread and receives strings from a + * producer thread via a shared BoundedBlockingQueue. + */ + private static class Consumer> + implements Runnable { + /** + * This queue is shared with the producer. + */ + private final BQ mQueue; + + /** + * Constructor initializes the @a blockingQueue data member. + */ + Consumer(BQ blockingQueue) { + mQueue = blockingQueue; + } + + /** + * This method runs in a Java thread and receives integers + * from a producer thread via a shared BoundedBlockingQueue. + */ + public void run() { + Integer integer = null; + int nullCount = 0; + + // Get the first item from the queue. + Integer previous; + + // Get the first non-null value. + while ((previous = mQueue.poll()) == null) + continue; + + mCount.decrementAndGet(); + + for (int i = 1; i < mMaxIterations; ) { + // Try to get the next integer. + integer = mQueue.poll(); + + // Only update the state if we get a non-null + // value from take(). + if (integer != null) { + if ((i % (mMaxIterations / 10)) == 0) + System.out.println(integer); + + mCount.decrementAndGet(); + i++; + + // Make sure the entries are ordered. + assertEquals(previous + 1, integer.intValue()); + previous = integer; + + } else { + nullCount++; + // Thread.yield(); + } + } + + assertEquals(0, mCount.get()); + + System.out.println("Final size of the queue is " + + mQueue.size() + + "\nmCount is " + + mCount.get() + + "\nFinal value is " + + integer + + "\nnumber of null returns from take() is " + + nullCount + + "\nmCount + nullCount is " + + (mCount.get() + nullCount)); + } + } + + /** + * Main entry point that tests the BusySynchronizedQueue class. + */ + @Test + public void testBusySynchronizedQueue() { + final BusySynchronizedQueue busySynchronizedQueue = + new BusySynchronizedQueue<>(sQUEUE_SIZE); + + try { + // Create producer and consumer threads. + Thread[] threads = new Thread[] { + new Thread(new Producer<>(busySynchronizedQueue)), + new Thread(new Consumer<>(busySynchronizedQueue)) + }; + + // Record the start time. + long startTime = System.nanoTime(); + + // Start all the threads. + for (Thread thread : threads) + thread.start(); + + // Wait for all threads to stop. + for (Thread thread : threads) + thread.join(); + + System.out.println("test ran in " + + (System.nanoTime() - startTime) / 1_000_000 + + " msecs"); + } catch (Exception e) { + System.out.println("caught exception"); + } + } +} diff --git a/SimpleBlockingQueue/.classpath b/SimpleBlockingQueue/.classpath deleted file mode 100644 index 18d70f02c..000000000 --- a/SimpleBlockingQueue/.classpath +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/SimpleBlockingQueue/.project b/SimpleBlockingQueue/.project deleted file mode 100644 index d5d34236a..000000000 --- a/SimpleBlockingQueue/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - SimpleBlockingQueue - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/SimpleBlockingQueue/build.gradle b/SimpleBlockingQueue/build.gradle new file mode 100644 index 000000000..d88d747f6 --- /dev/null +++ b/SimpleBlockingQueue/build.gradle @@ -0,0 +1,15 @@ +group 'livelessons' +version '1.0-SNAPSHOT' + +apply plugin: 'java' + +targetCompatibility = 1.8 +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.11' +} diff --git a/SimpleBlockingQueue/gradle/wrapper/gradle-wrapper.jar b/SimpleBlockingQueue/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..ca78035ef Binary files /dev/null and b/SimpleBlockingQueue/gradle/wrapper/gradle-wrapper.jar differ diff --git a/SimpleBlockingQueue/gradle/wrapper/gradle-wrapper.properties b/SimpleBlockingQueue/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..be3fb022c --- /dev/null +++ b/SimpleBlockingQueue/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Aug 16 18:00:52 EDT 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip diff --git a/SimpleBlockingQueue/gradlew b/SimpleBlockingQueue/gradlew new file mode 100644 index 000000000..27309d923 --- /dev/null +++ b/SimpleBlockingQueue/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/SimpleBlockingQueue/gradlew.bat b/SimpleBlockingQueue/gradlew.bat new file mode 100644 index 000000000..832fdb607 --- /dev/null +++ b/SimpleBlockingQueue/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/SimpleBlockingQueue/settings.gradle b/SimpleBlockingQueue/settings.gradle new file mode 100644 index 000000000..55df1dc07 --- /dev/null +++ b/SimpleBlockingQueue/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'SimpleBlockingBoundedQueue' + diff --git a/SimpleBlockingQueue/src/SimpleBlockingQueue.java b/SimpleBlockingQueue/src/SimpleBlockingQueue.java deleted file mode 100644 index 79ce09321..000000000 --- a/SimpleBlockingQueue/src/SimpleBlockingQueue.java +++ /dev/null @@ -1,189 +0,0 @@ -import java.util.List; -import java.util.concurrent.CyclicBarrier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.TimeUnit; - -/* - * @class SimpleBlockingQueue - * - * @brief Defines an implementation of the BlockingQueue interface - * that works properly when accessed via multiple threads since - * it's synchronized properly. - */ -class SimpleBlockingQueue implements BlockingQueue { - /** - * The queue consists of a List of E's. - */ - final private List mList; - - /** - * The maximum capacity of the queue or Integer.MAX_VALUE if none. - */ - private final int mCapacity; - - /** - * Create a SimpleBlocking queue with a capacity of - * Integer.MAX_VALUE. - */ - public SimpleBlockingQueue() { - this(Integer.MAX_VALUE); - } - - /** - * Create a SimpleBlocking queue with the given capacity. - */ - public SimpleBlockingQueue(int capacity) { - if (capacity <= 0) - throw new IllegalArgumentException(); - mCapacity = capacity; - mList = new ArrayList(); - } - - /** - * Add a new E to the end of the queue, blocking if necessary for - * space to become available. - */ - public void put(E e) throws InterruptedException { - synchronized(this) { - if (e == null) - throw new NullPointerException(); - - // Wait until the queue is not full. - while (isFull()) { - // System.out.println("BLOCKING ON PUT()"); - wait(); - } - - // Add e to the ArrayList. - mList.add(e); - - // Notify that the queue may have changed state, e.g., "no - // longer empty". - notifyAll(); - } - } - - /** - * Remove the E at the front of the queue, blocking until there's - * something in the queue. - */ - public E take() throws InterruptedException { - synchronized(this) { - // Wait until the queue is not empty. - while (mList.isEmpty()) { - // System.out.println("BLOCKING ON TAKE()"); - wait(); - } - - final E e = mList.remove(0); - - // Notify that the queue may have changed state, e.g., "no - // longer full". - notifyAll(); - return e; - } - } - - /** - * Returns the number of elements in this queue. - */ - public int size() { - synchronized(this) { - return mList.size(); - } - } - - /** - * Returns true if the queue is empty, else false. - */ - public boolean isEmpty() { - synchronized(this) { - return mList.size() == 0; - } - } - - /** - * Returns true if the queue is full, else false. Since this - * isn't a public method it assumes the monitor lock is held. - */ - private boolean isFull() { - return mList.size() == mCapacity; - } - - /** - * All these methods are inherited from the BlockingQueue - * interface. They are defined as no-ops and their implementations - * are left as an exercise to the reader. - */ - public int drainTo(Collection c) { - return 0; - } - public int drainTo(Collection c, int maxElements) { - return 0; - } - public boolean contains(Object o) { - return false; - } - public boolean remove(Object o) { - return false; - } - public int remainingCapacity() { - return 0; - } - public E poll() { - return null; - } - public E poll(long timeout, TimeUnit unit) throws InterruptedException { - return take(); - } - public E peek() { - return null; - } - public boolean offer(E e) { - return false; - } - public boolean offer(E e, long timeout, TimeUnit unit) { - try { - put(e); - } - catch (InterruptedException ex) { - // Just swallow this exception for this simple (buggy) test. - } - return true; - } - public boolean add(E e) { - return false; - } - public E element() { - return null; - } - public E remove() { - return null; - } - public void clear() { - } - public boolean retainAll(Collection collection) { - return false; - } - public boolean removeAll(Collection collection) { - return false; - } - public boolean addAll(Collection collection) { - return false; - } - public boolean containsAll(Collection collection) { - return false; - } - public Object[] toArray() { - return null; - } - public T[] toArray(T[] array) { - return null; - } - public Iterator iterator() { - return null; - } -} diff --git a/SimpleBlockingQueue/src/SimpleBlockingQueueTest.java b/SimpleBlockingQueue/src/SimpleBlockingQueueTest.java deleted file mode 100644 index 854bdb583..000000000 --- a/SimpleBlockingQueue/src/SimpleBlockingQueueTest.java +++ /dev/null @@ -1,147 +0,0 @@ -import java.util.concurrent.*; -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.List; - -/** - * @class SimpleBlockingQueueTest - * - * @brief Test program for the SimpleBlockingQueue that induces race - * conditions due to lack of synchronization. - */ -public class SimpleBlockingQueueTest -{ - /** - * Maximum number of iterations. - */ - private final static int mMaxIterations = 100000; - - /** - * Maximum size of the queue. - */ - private final static int mQueueSize = 10; - - /** - * Count the number of iterations. - */ - private final static AtomicInteger mCount = - new AtomicInteger(0); - - /** - * @class ProducerThread - * - * @brief This producer runs in a separate Java Thread and passes - * Strings to a consumer Thread via a shared BlockingQueue. - */ - static class ProducerThread extends Thread { - /** - * This queue is shared with the consumer. - */ - private final BQ mQueue; - - /** - * Constructor initializes the BlockingQueue data - * member. - */ - ProducerThread(BQ blockingQueue) { - mQueue = blockingQueue; - } - - /** - * This method runs in a separate Java Thread and passes - * Strings to a consumer Thread via a shared BlockingQueue. - */ - public void run(){ - try { - for(int i = 0; i < mMaxIterations; i++) { - mCount.incrementAndGet(); - - // Calls the put() method. - mQueue.put(Integer.toString(i)); - } - } catch (InterruptedException e) { - System.out.println("InterruptedException caught"); - } - } - } - - /** - * @class ConsumerThread - * - * @brief This consumer runs in a separate Java Thread and - * receives Strings from a producer Thread via a shared - * BlockingQueue. - */ - static class ConsumerThread extends Thread { - /** - * This queue is shared with the producer. - */ - private final BQ mQueue; - - /** - * Constructor initializes the BlockingQueue data member. - */ - ConsumerThread(BQ blockingQueue) { - mQueue = blockingQueue; - } - - /** - * This method runs in a separate Java Thread and receives - * Strings from a producer Thread via a shared BlockingQueue. - */ - public void run(){ - Object s = null; - try { - for(int i = 0; i < mMaxIterations; i++) { - // Calls the take() method. - s = mQueue.take(); - - mCount.decrementAndGet(); - - if((i % (mMaxIterations / 10)) == 0) - System.out.println(s); - } - } catch (InterruptedException e) { - System.out.println("InterruptedException caught"); - } - System.out.println("Final size of the queue is " - + mQueue.size() - + "\nmCount is " - + mCount.get() - + "\nFinal value is " - + s); - } - } - - /** - * Main entry point that tests the SimpleBlockingQueue class. - */ - public static void main(String argv[]) { - final SimpleBlockingQueue simpleQueue = - new SimpleBlockingQueue(mQueueSize); - - try { - // Create a ProducerThread. - Thread producer = - new ProducerThread(simpleQueue); - - // Create a ConsumerThread. - Thread consumer = - new ConsumerThread(simpleQueue); - - // Run both Threads concurrently. - producer.start(); - try { - Thread.sleep(100); - } catch (InterruptedException e) {} - - consumer.start(); - - // Wait for both Threads to stop. - producer.join(); - consumer.join(); - } catch (Exception e) { - System.out.println("caught exception"); - } - } -} diff --git a/SimpleBlockingQueue/src/main/java/edu/vandy/BoundedQueue.java b/SimpleBlockingQueue/src/main/java/edu/vandy/BoundedQueue.java new file mode 100644 index 000000000..c80a8109a --- /dev/null +++ b/SimpleBlockingQueue/src/main/java/edu/vandy/BoundedQueue.java @@ -0,0 +1,76 @@ +package edu.vandy; + +/** + * Defines an interface for a bounded queue. + */ +public interface BoundedQueue { + /** + * Inserts the specified element into this queue, waiting if + * necessary for space to become available. + * + * @param e the element to add + * @throws InterruptedException if interrupted while waiting + */ + default void put(E e) + throws InterruptedException { + } + + /** + * Retrieves and removes the head of this queue, waiting if necessary + * until an element becomes available. + * + * @return the head of this queue + * @throws InterruptedException if interrupted while waiting + */ + default E take() + throws InterruptedException { + return null; + } + + /** + * Retrieves and removes the head of this queue, or returns {@code + * null} if this queue is empty. + * + * @return the head of this queue, or {@code null} if this queue is empty + */ + default E poll() { + return null; + } + + /** + * Inserts the specified element into this queue if it is possible to do + * so immediately without violating capacity restrictions, returning + * {@code true} upon success and {@code false} if no space is currently + * available. + * + * @return {@code true} if the element was added to this queue, else + * {@code false} + */ + default boolean offer(E e) { + return false; + } + + /** + * Returns true if this queue contains no elements, else false. + * + * @return true if this queue contains no elements, else false. + */ + boolean isEmpty(); + + /** + * Returns true if this queue is full, else false. + * + * @return true if this queue is full, else false. + */ + boolean isFull(); + + /** + * Returns the number of elements in this queue. + * + * @return the number of elements in this collection + */ + int size(); +} + + + diff --git a/SimpleBlockingQueue/src/main/java/edu/vandy/SimpleBlockingBoundedQueue.java b/SimpleBlockingQueue/src/main/java/edu/vandy/SimpleBlockingBoundedQueue.java new file mode 100644 index 000000000..df0299739 --- /dev/null +++ b/SimpleBlockingQueue/src/main/java/edu/vandy/SimpleBlockingBoundedQueue.java @@ -0,0 +1,126 @@ +package edu.vandy; + +import java.util.List; +import java.util.LinkedList; +import java.util.Collection; +import java.util.Iterator; +import java.util.concurrent.TimeUnit; + +/* + * Defines an implementation of the BoundedQueue interface that works + * properly when accessed via multiple threads since it's synchronized + * properly. + */ +class SimpleBlockingBoundedQueue + implements BoundedQueue { + /** + * The queue consists of a LinkedList of E's. + */ + final private List mList; + + /** + * The maximum capacity of the queue or Integer.MAX_VALUE if none. + */ + private final int mCapacity; + + /** + * Create a SimpleBlocking queue with a capacity of + * Integer.MAX_VALUE. + */ + public SimpleBlockingBoundedQueue() { + this(Integer.MAX_VALUE); + } + + /** + * Create a SimpleBlocking queue with the given capacity. + */ + public SimpleBlockingBoundedQueue(int capacity) { + if (capacity <= 0) + throw new IllegalArgumentException(); + mCapacity = capacity; + mList = new LinkedList<>(); + } + + /** + * Inserts the specified element into this queue, waiting if + * necessary for space to become available. + * + * @param e the element to add + * @throws InterruptedException if interrupted while waiting + */ + @Override + public void put(E e) + throws InterruptedException { + synchronized(this) { + if (e == null) + throw new NullPointerException(); + + // Wait until the queue is not full. + while (isFull()) { + // System.out.println("BLOCKING ON PUT()"); + wait(); + } + + // Add e to the LinkedList. + mList.add(e); + + // Notify that the queue may have changed state, e.g., "no + // longer empty". + notifyAll(); + } + } + + /** + * Retrieves and removes the head of this queue, waiting if necessary + * until an element becomes available. + * + * @return the head of this queue + * @throws InterruptedException if interrupted while waiting + */ + @Override + public E take() throws InterruptedException { + synchronized(this) { + // Wait until the queue is not empty. + while (mList.isEmpty()) { + // System.out.println("BLOCKING ON TAKE()"); + wait(); + } + + final E e = mList.remove(0); + + // Notify that the queue may have changed state, e.g., "no + // longer full". + notifyAll(); + return e; + } + } + + /** + * Returns true if the queue is empty, else false. + */ + @Override + public boolean isEmpty() { + synchronized(this) { + return mList.size() == 0; + } + } + + /** + * Returns true if the queue is full, else false. Since this + * isn't a public method it assumes the monitor lock is held. + */ + @Override + public boolean isFull() { + return mList.size() == mCapacity; + } + + /** + * Returns the number of elements in this queue. + */ + @Override + public int size() { + synchronized(this) { + return mList.size(); + } + } +} diff --git a/SimpleBlockingQueue/src/test/java/edu/vandy/SimpleBlockingBoundedQueueTest.java b/SimpleBlockingQueue/src/test/java/edu/vandy/SimpleBlockingBoundedQueueTest.java new file mode 100644 index 000000000..7129f17d8 --- /dev/null +++ b/SimpleBlockingQueue/src/test/java/edu/vandy/SimpleBlockingBoundedQueueTest.java @@ -0,0 +1,158 @@ +package edu.vandy; + +import static org.junit.Assert.assertEquals; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +/** + * Test program for the SimpleBlockingBoundedQueue that fixes race conditions + * by having proper synchronization (i.e., mutual exclusion and + * coordination). + */ +public class SimpleBlockingBoundedQueueTest { + /** + * Maximum number of iterations. + */ + private final static int mMaxIterations = 100000; + + /** + * Maximum size of the queue. + */ + private final static int sQUEUE_SIZE = 10; + + /** + * Count the number of iterations. + */ + private final static AtomicInteger mCount = + new AtomicInteger(0); + + /** + * This producer runs in a separate Java thread and passes integers + * to a consumer thread via a shared BoundedQueue. + */ + private static class Producer> + implements Runnable { + /** + * This queue is shared with the consumer. + */ + private final BQ mQueue; + + /** + * Constructor initializes the BoundedQueue data + * member. + */ + Producer(BQ blockingQueue) { + mQueue = blockingQueue; + } + + /** + * This method runs in a separate Java thread and passes + * integers to a consumer thread via a shared BoundedQueue. + */ + public void run(){ + try { + for (int i = 0; i < mMaxIterations; i++) { + mCount.incrementAndGet(); + + // Call the put() method. + mQueue.put(i); + } + } catch (InterruptedException e) { + System.out.println("InterruptedException caught"); + } + } + } + + /** + * This consumer runs in a separate Java thread and receives + * integers from a producer thread via a shared BoundedQueue. + */ + private static class Consumer> + implements Runnable { + /** + * This queue is shared with the producer. + */ + private final BQ mQueue; + + /** + * Constructor initializes the BoundedQueue data member. + */ + Consumer(BQ blockingQueue) { + mQueue = blockingQueue; + } + + /** + * This method runs in a separate Java thread and receives + * integers from a producer thread[q via a shared BoundedQueue. + */ + public void run() { + Integer integer = null; + + try { + // Get the first item from the queue. + Integer previous = mQueue.take(); + mCount.decrementAndGet(); + + for (int i = 1; i < mMaxIterations; ++i) { + // Calls the take() method. + integer = mQueue.take(); + + // Make sure the entries are ordered. + assertEquals(previous + 1, integer.intValue()); + previous = integer; + + if((i % (mMaxIterations / 10)) == 0) + System.out.println(integer); + + mCount.decrementAndGet(); + } + } catch (InterruptedException e) { + System.out.println("InterruptedException caught"); + } + assertEquals(0, mCount.get()); + + System.out.println("Final size of the queue is " + + mQueue.size() + + "\nmCount is " + + mCount.get() + + "\nFinal value is " + + integer); + } + } + + /** + * Main entry point that tests the SimpleBoundedQueue class. + */ + @Test + public void testSimpleBlockingBoundedQueue() { + final SimpleBlockingBoundedQueue simpleQueue = + new SimpleBlockingBoundedQueue<>(sQUEUE_SIZE); + + try { + // Create producer and consumer threads. + Thread[] threads = new Thread[] { + new Thread(new Producer<>(simpleQueue)), + new Thread(new Consumer<>(simpleQueue)) + }; + + // Record the start time. + long startTime = System.nanoTime(); + + // Start both threads. + for (Thread thread : threads) + thread.start(); + + // Wait for both threads to stop. + for (Thread thread : threads) + thread.join(); + + System.out.println("test ran in " + + (System.nanoTime() - startTime) / 1_000_000 + + " msecs"); + } catch (Exception e) { + System.out.println("caught exception"); + } + } +}