From d9a23b16be44703671224ba27ccbc141000666ab Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Tue, 27 Feb 2018 02:32:58 +0300 Subject: [PATCH 01/19] initial commit --- HW1-Lazy/build.gradle | 89 +++++++++ HW1-Lazy/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + HW1-Lazy/gradlew | 172 ++++++++++++++++++ HW1-Lazy/gradlew.bat | 84 +++++++++ HW1-Lazy/settings.gradle | 2 + HW1-Lazy/src/main/java/Hello.java | 4 + HW1-Lazy/src/test/java/HelloTest.java | 12 ++ 8 files changed, 369 insertions(+) create mode 100644 HW1-Lazy/build.gradle create mode 100644 HW1-Lazy/gradle/wrapper/gradle-wrapper.jar create mode 100644 HW1-Lazy/gradle/wrapper/gradle-wrapper.properties create mode 100755 HW1-Lazy/gradlew create mode 100644 HW1-Lazy/gradlew.bat create mode 100644 HW1-Lazy/settings.gradle create mode 100644 HW1-Lazy/src/main/java/Hello.java create mode 100644 HW1-Lazy/src/test/java/HelloTest.java diff --git a/HW1-Lazy/build.gradle b/HW1-Lazy/build.gradle new file mode 100644 index 0000000..906d587 --- /dev/null +++ b/HW1-Lazy/build.gradle @@ -0,0 +1,89 @@ +group 'JavaHW' +version '1.0-SNAPSHOT' + +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.2' + classpath 'com.google.guava:guava:23.5-jre' + classpath 'org.mockito:mockito-all:2.0.2-beta' + } +} + +repositories { + mavenCentral() +} + +ext.junit4Version = '4.12' +ext.junitVintageVersion = '4.12.2' +ext.junitPlatformVersion = '1.0.2' +ext.junitJupiterVersion = '5.0.2' +ext.log4jVersion = '2.9.0' + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'idea' +apply plugin: 'org.junit.platform.gradle.plugin' + +jar { + baseName = 'junit5-gradle-consumer' + version = '1.0.0-SNAPSHOT' +} + +compileTestJava { + sourceCompatibility = 1.9 + targetCompatibility = 1.9 + options.compilerArgs += '-parameters' +} + +junitPlatform { + // platformVersion '1.0.2' + filters { + engines { + // include 'junit-jupiter', 'junit-vintage' + // exclude 'custom-engine' + } + tags { + // include 'fast' + exclude 'slow' + } + // includeClassNamePattern '.*Test' + } + // configurationParameter 'junit.jupiter.conditions.deactivate', '*' + // enableStandardTestTask true + // reportsDir file('build/test-results/junit-platform') // this is the default + logManager 'org.apache.logging.log4j.jul.LogManager' +} + +dependencies { + // JUnit Jupiter API and TestEngine implementation + testCompile("org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}") + testRuntime("org.junit.jupiter:junit-jupiter-engine:${junitJupiterVersion}") + + // If you also want to support JUnit 3 and JUnit 4 tests + testCompile("junit:junit:${junit4Version}") + testRuntime("org.junit.vintage:junit-vintage-engine:${junitVintageVersion}") + + // To avoid compiler warnings about @API annotations in JUnit code + testCompileOnly('org.apiguardian:apiguardian-api:1.0.0') + + // To use Log4J's LogManager + testRuntime("org.apache.logging.log4j:log4j-core:${log4jVersion}") + testRuntime("org.apache.logging.log4j:log4j-jul:${log4jVersion}") + + // Only needed to run tests in an (IntelliJ) IDE(A) that bundles an older version + testRuntime("org.junit.platform:junit-platform-launcher:${junitPlatformVersion}") + + testCompile group: 'org.hamcrest', name: 'hamcrest-junit', version: '2.0.0.0' + compile group: 'org.jetbrains', name: 'annotations', version: '15.0' + + compile group: 'com.google.guava', name: 'guava', version: '23.5-jre' + testCompile group: 'org.mockito', name: 'mockito-all', version: '2.0.2-beta' +} + +task wrapper(type: Wrapper) { + description = 'Generates gradlew[.bat] scripts' + gradleVersion = '4.3.1' +} \ No newline at end of file diff --git a/HW1-Lazy/gradle/wrapper/gradle-wrapper.jar b/HW1-Lazy/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..01b8bf6b1f99cad9213fc495b33ad5bbab8efd20 GIT binary patch literal 54329 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

<5G2M1WZP5 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 \(.*\)$'` + 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 + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/HW1-Lazy/gradlew.bat b/HW1-Lazy/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/HW1-Lazy/gradlew.bat @@ -0,0 +1,84 @@ +@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 + +: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=%* + +: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/HW1-Lazy/settings.gradle b/HW1-Lazy/settings.gradle new file mode 100644 index 0000000..3ea267a --- /dev/null +++ b/HW1-Lazy/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'HW1-Lazy' + diff --git a/HW1-Lazy/src/main/java/Hello.java b/HW1-Lazy/src/main/java/Hello.java new file mode 100644 index 0000000..fc7b3b9 --- /dev/null +++ b/HW1-Lazy/src/main/java/Hello.java @@ -0,0 +1,4 @@ +public class Hello { + + +} diff --git a/HW1-Lazy/src/test/java/HelloTest.java b/HW1-Lazy/src/test/java/HelloTest.java new file mode 100644 index 0000000..d9eedab --- /dev/null +++ b/HW1-Lazy/src/test/java/HelloTest.java @@ -0,0 +1,12 @@ +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class HelloTest { + + @Test + void test() { + assertTrue(true); + } + +} \ No newline at end of file From e4d9066e3bc14857af97d0ddb6d63280900d11cb Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Wed, 28 Feb 2018 00:27:11 +0300 Subject: [PATCH 02/19] Lazy interface and LazyFactory (no tests yet) --- HW1-Lazy/src/main/java/Hello.java | 4 - .../java/ru/spbau/mit/kirakosian/Lazy.java | 19 ++++ .../ru/spbau/mit/kirakosian/LazyFactory.java | 91 +++++++++++++++++++ 3 files changed, 110 insertions(+), 4 deletions(-) delete mode 100644 HW1-Lazy/src/main/java/Hello.java create mode 100644 HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/Lazy.java create mode 100644 HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/LazyFactory.java diff --git a/HW1-Lazy/src/main/java/Hello.java b/HW1-Lazy/src/main/java/Hello.java deleted file mode 100644 index fc7b3b9..0000000 --- a/HW1-Lazy/src/main/java/Hello.java +++ /dev/null @@ -1,4 +0,0 @@ -public class Hello { - - -} diff --git a/HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/Lazy.java b/HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/Lazy.java new file mode 100644 index 0000000..e84252a --- /dev/null +++ b/HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/Lazy.java @@ -0,0 +1,19 @@ +package ru.spbau.mit.kirakosian; + +/** + * Interface to store exactly one value and provide lazy evaluations. It means, that target + * value will be evaluated only one time, on the first get method call. + * + * This class in some way similar to Singleton pattern. + * + * @param type of the stored element. + */ +public interface Lazy { + + /** + * Returns stored in Lazy value. Evaluates this value if it is not known yet, + * otherwise returns already evaluated value. + */ + T get(); + +} diff --git a/HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/LazyFactory.java b/HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/LazyFactory.java new file mode 100644 index 0000000..0ece17d --- /dev/null +++ b/HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/LazyFactory.java @@ -0,0 +1,91 @@ +package ru.spbau.mit.kirakosian; + +import java.util.function.Supplier; + +/** + * Factory class to produce different ru.spbau.mit.kirakosian.Lazy classes. Unsynchronized and synchronized versions + * are supported. + */ +public class LazyFactory { + + /** + * Basic method to create unsynchronized lazy class from the given supplier. + * + * @param supplier supplier to get element from. + * @param type of the stored in ru.spbau.mit.kirakosian.Lazy element + * @return new concurrent unsafe ru.spbau.mit.kirakosian.Lazy instance + */ + public static Lazy createUnsynchronizedLazy(final Supplier supplier) { + // there was a variant to use anonymous class instead of inner static, + // but with this implementation our code is a bit more flexible. For example, if + // we want to support something else instead of supplier we may use the same class, + // but with the different constructor. + return new LazyUnsync<>(supplier); + } + + /** + * Basic method to create synchronized lazy class from the given supplier. + * + * @param supplier supplier to get element from. + * @param type of the stored in ru.spbau.mit.kirakosian.Lazy element + * @return new concurrent safe ru.spbau.mit.kirakosian.Lazy instance + */ + public static Lazy createSynchronizedLazy(final Supplier supplier) { + return new LazySync<>(supplier); + } + + /** + * Basic ru.spbau.mit.kirakosian.Lazy implementation. This class is not recommended to use in concurrent programs, + * use LazySync implementation instead. + * + * @param type of the stored value + */ + private static class LazyUnsync implements Lazy { + + private Supplier supplier; + private T value; + + LazyUnsync(final Supplier supplier) { + this.supplier = supplier; + } + + // as I see, if there is no description the one from the interface will be shown + @Override + public T get() { + if (supplier != null) { + value = supplier.get(); + supplier = null; + } + return value; + } + } + + /** + * Synchronized version of LazyUnsync class. Supports correct work with many threads. + * + * @param type the of stored value. + */ + private static class LazySync implements Lazy { + + private Supplier supplier; + private T value; + + LazySync(final Supplier supplier) { + this.supplier = supplier; + } + + @Override + public T get() { + if (supplier != null) { + synchronized (this) { + if (supplier != null) { // check that we have not done this section yet + value = supplier.get(); + supplier = null; + } + } + } + return value; + } + } + +} From bdba7dd7060d112236e439f2cd0f739a93403f71 Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Wed, 28 Feb 2018 00:34:55 +0300 Subject: [PATCH 03/19] comment for get method was fixed --- .../src/main/java/ru/spbau/mit/kirakosian/LazyFactory.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/LazyFactory.java b/HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/LazyFactory.java index 0ece17d..bfae724 100644 --- a/HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/LazyFactory.java +++ b/HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/LazyFactory.java @@ -49,7 +49,8 @@ private static class LazyUnsync implements Lazy { this.supplier = supplier; } - // as I see, if there is no description the one from the interface will be shown + // as i see, if there is no description the one from the interface will be shown + // it looks more beautiful then the link in my opinion @Override public T get() { if (supplier != null) { From 33ef1fd7467ddf76005f30ea4a36303bd8c0623a Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Wed, 28 Feb 2018 17:30:35 +0300 Subject: [PATCH 04/19] tests for Lazy (with docs) --- .../ru/spbau/mit/kirakosian/LazyFactory.java | 9 ++- HW1-Lazy/src/test/java/HelloTest.java | 12 --- .../spbau/mit/kirakosian/CounterSupplier.java | 41 +++++++++++ .../spbau/mit/kirakosian/LazyFactoryTest.java | 73 +++++++++++++++++++ 4 files changed, 121 insertions(+), 14 deletions(-) delete mode 100644 HW1-Lazy/src/test/java/HelloTest.java create mode 100644 HW1-Lazy/src/test/java/ru/spbau/mit/kirakosian/CounterSupplier.java create mode 100644 HW1-Lazy/src/test/java/ru/spbau/mit/kirakosian/LazyFactoryTest.java diff --git a/HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/LazyFactory.java b/HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/LazyFactory.java index bfae724..5e0dff2 100644 --- a/HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/LazyFactory.java +++ b/HW1-Lazy/src/main/java/ru/spbau/mit/kirakosian/LazyFactory.java @@ -1,11 +1,14 @@ package ru.spbau.mit.kirakosian; +import org.jetbrains.annotations.Nullable; + import java.util.function.Supplier; /** * Factory class to produce different ru.spbau.mit.kirakosian.Lazy classes. Unsynchronized and synchronized versions * are supported. */ +@SuppressWarnings("WeakerAccess") public class LazyFactory { /** @@ -42,10 +45,11 @@ public static Lazy createSynchronizedLazy(final Supplier supplier) { */ private static class LazyUnsync implements Lazy { + @Nullable private Supplier supplier; private T value; - LazyUnsync(final Supplier supplier) { + LazyUnsync(@Nullable final Supplier supplier) { this.supplier = supplier; } @@ -68,10 +72,11 @@ public T get() { */ private static class LazySync implements Lazy { + @Nullable private Supplier supplier; private T value; - LazySync(final Supplier supplier) { + LazySync(@Nullable final Supplier supplier) { this.supplier = supplier; } diff --git a/HW1-Lazy/src/test/java/HelloTest.java b/HW1-Lazy/src/test/java/HelloTest.java deleted file mode 100644 index d9eedab..0000000 --- a/HW1-Lazy/src/test/java/HelloTest.java +++ /dev/null @@ -1,12 +0,0 @@ -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -class HelloTest { - - @Test - void test() { - assertTrue(true); - } - -} \ No newline at end of file diff --git a/HW1-Lazy/src/test/java/ru/spbau/mit/kirakosian/CounterSupplier.java b/HW1-Lazy/src/test/java/ru/spbau/mit/kirakosian/CounterSupplier.java new file mode 100644 index 0000000..2ae0e74 --- /dev/null +++ b/HW1-Lazy/src/test/java/ru/spbau/mit/kirakosian/CounterSupplier.java @@ -0,0 +1,41 @@ +package ru.spbau.mit.kirakosian; + +import java.util.function.Supplier; + +/** + * Simple supplier that returns target value on every get method call. + * + * In addition, counts number of get method calls. + * + * @param type of the stored value. + */ +@SuppressWarnings("WeakerAccess") +class CounterSupplier implements Supplier { + + private int counter; + private final T value; + + /** + * Constructs supplier with the target value. + * + * @param value value to return on get method call. + */ + public CounterSupplier(final T value) { + this.value = value; + } + + /** + * Access to the counter. + * + * @return number of get method calls. + */ + public int counter() { + return counter; + } + + @Override + public T get() { + counter++; + return value; + } +} diff --git a/HW1-Lazy/src/test/java/ru/spbau/mit/kirakosian/LazyFactoryTest.java b/HW1-Lazy/src/test/java/ru/spbau/mit/kirakosian/LazyFactoryTest.java new file mode 100644 index 0000000..a6da585 --- /dev/null +++ b/HW1-Lazy/src/test/java/ru/spbau/mit/kirakosian/LazyFactoryTest.java @@ -0,0 +1,73 @@ +package ru.spbau.mit.kirakosian; + +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; + +import java.util.LinkedList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +@SuppressWarnings("FieldCanBeLocal") +class LazyFactoryTest { + + private final int threadsNumber = 10; + private final int testsNumber = 100; + + @Test + void testCreateUnsynchronizedLazye() { + final CounterSupplier stringSupplier = new CounterSupplier<>("hello"); + final CounterSupplier nullSupplier = new CounterSupplier<>(null); + + assertEquals(stringSupplier.counter(), 0); + assertEquals(nullSupplier.counter(), 0); + + final Lazy stringLazy = LazyFactory.createUnsynchronizedLazy(stringSupplier); + final Lazy nullLazy = LazyFactory.createUnsynchronizedLazy(nullSupplier); + + assertEquals("hello", stringLazy.get()); + assertEquals("hello", stringLazy.get()); + assertEquals("hello", stringLazy.get()); + + assertNull(nullLazy.get()); + assertNull(nullLazy.get()); + assertNull(nullLazy.get()); + assertNull(nullLazy.get()); + + assertEquals(1, stringSupplier.counter()); + assertEquals(1, nullSupplier.counter()); + } + + @Test + void testCreateSynchronizedLazy() { + for (int i = 0; i < testsNumber; i++) { + try { + oneCreateSynchronizedLazyTest("hello"); + oneCreateSynchronizedLazyTest(null); + } catch (@NotNull final InterruptedException e) { + // no idea what to do here... It is not a failure + } + } + } + + private void oneCreateSynchronizedLazyTest(final String testValue) throws InterruptedException { + final CounterSupplier supplier = new CounterSupplier<>(testValue); + final Lazy lazy = LazyFactory.createSynchronizedLazy(supplier); + + final List tasks = new LinkedList<>(); + for (int i = 0; i < threadsNumber; i++) { + tasks.add(new Thread(() -> assertEquals(testValue, lazy.get()))); + } + + for (final Thread task : tasks) { + task.start(); + } + + for (final Thread task : tasks) { + task.join(); + } + assertEquals(supplier.counter(), 1); + } + +} \ No newline at end of file From 158a046226befef225f52e7f383b463e34eca7a2 Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Wed, 28 Feb 2018 17:45:41 +0300 Subject: [PATCH 05/19] initial for ThreadPool --- ThreadPool/build.gradle | 104 +++++++++++ ThreadPool/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + ThreadPool/gradlew | 172 ++++++++++++++++++ ThreadPool/gradlew.bat | 84 +++++++++ ThreadPool/settings.gradle | 2 + 6 files changed, 368 insertions(+) create mode 100644 ThreadPool/build.gradle create mode 100644 ThreadPool/gradle/wrapper/gradle-wrapper.jar create mode 100644 ThreadPool/gradle/wrapper/gradle-wrapper.properties create mode 100755 ThreadPool/gradlew create mode 100644 ThreadPool/gradlew.bat create mode 100644 ThreadPool/settings.gradle diff --git a/ThreadPool/build.gradle b/ThreadPool/build.gradle new file mode 100644 index 0000000..d730946 --- /dev/null +++ b/ThreadPool/build.gradle @@ -0,0 +1,104 @@ +group 'JavaHW' +version '1.0-SNAPSHOT' + +group 'JavaHW' +version '1.0-SNAPSHOT' + +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.2' + classpath 'com.google.guava:guava:23.5-jre' + classpath 'org.mockito:mockito-all:2.0.2-beta' + } +} + +repositories { + mavenCentral() +} + +ext.junit4Version = '4.12' +ext.junitVintageVersion = '4.12.2' +ext.junitPlatformVersion = '1.0.2' +ext.junitJupiterVersion = '5.0.2' +ext.log4jVersion = '2.9.0' + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'idea' +apply plugin: 'org.junit.platform.gradle.plugin' + +jar { + baseName = 'junit5-gradle-consumer' + version = '1.0.0-SNAPSHOT' +} + +compileTestJava { + sourceCompatibility = 1.9 + targetCompatibility = 1.9 + options.compilerArgs += '-parameters' +} + +junitPlatform { + // platformVersion '1.0.2' + filters { + engines { + // include 'junit-jupiter', 'junit-vintage' + // exclude 'custom-engine' + } + tags { + // include 'fast' + exclude 'slow' + } + // includeClassNamePattern '.*Test' + } + // configurationParameter 'junit.jupiter.conditions.deactivate', '*' + // enableStandardTestTask true + // reportsDir file('build/test-results/junit-platform') // this is the default + logManager 'org.apache.logging.log4j.jul.LogManager' +} + +dependencies { + // JUnit Jupiter API and TestEngine implementation + testCompile("org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}") + testRuntime("org.junit.jupiter:junit-jupiter-engine:${junitJupiterVersion}") + + // If you also want to support JUnit 3 and JUnit 4 tests + testCompile("junit:junit:${junit4Version}") + testRuntime("org.junit.vintage:junit-vintage-engine:${junitVintageVersion}") + + // To avoid compiler warnings about @API annotations in JUnit code + testCompileOnly('org.apiguardian:apiguardian-api:1.0.0') + + // To use Log4J's LogManager + testRuntime("org.apache.logging.log4j:log4j-core:${log4jVersion}") + testRuntime("org.apache.logging.log4j:log4j-jul:${log4jVersion}") + + // Only needed to run tests in an (IntelliJ) IDE(A) that bundles an older version + testRuntime("org.junit.platform:junit-platform-launcher:${junitPlatformVersion}") + + testCompile group: 'org.hamcrest', name: 'hamcrest-junit', version: '2.0.0.0' + compile group: 'org.jetbrains', name: 'annotations', version: '15.0' + + compile group: 'com.google.guava', name: 'guava', version: '23.5-jre' + testCompile group: 'org.mockito', name: 'mockito-all', version: '2.0.2-beta' +} + +task wrapper(type: Wrapper) { + description = 'Generates gradlew[.bat] scripts' + gradleVersion = '4.3.1' +} + +apply plugin: 'java' + +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' +} diff --git a/ThreadPool/gradle/wrapper/gradle-wrapper.jar b/ThreadPool/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..01b8bf6b1f99cad9213fc495b33ad5bbab8efd20 GIT binary patch literal 54329 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

    <5G2M1WZP5 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 \(.*\)$'` + 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 + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/ThreadPool/gradlew.bat b/ThreadPool/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/ThreadPool/gradlew.bat @@ -0,0 +1,84 @@ +@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 + +: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=%* + +: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/ThreadPool/settings.gradle b/ThreadPool/settings.gradle new file mode 100644 index 0000000..abed208 --- /dev/null +++ b/ThreadPool/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'ThreadPool' + From fc66f5c1054342cf04b3ca1d69060d3a699c1255 Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Wed, 28 Feb 2018 17:56:22 +0300 Subject: [PATCH 06/19] threadPool basic classes --- ThreadPool/src/main/java/ThreadPool.java | 5 ++++ .../src/main/java/ThreadPoolFactory.java | 26 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 ThreadPool/src/main/java/ThreadPool.java create mode 100644 ThreadPool/src/main/java/ThreadPoolFactory.java diff --git a/ThreadPool/src/main/java/ThreadPool.java b/ThreadPool/src/main/java/ThreadPool.java new file mode 100644 index 0000000..e6c4fbe --- /dev/null +++ b/ThreadPool/src/main/java/ThreadPool.java @@ -0,0 +1,5 @@ +public interface ThreadPool { + + void addTask(Runnable task); + +} diff --git a/ThreadPool/src/main/java/ThreadPoolFactory.java b/ThreadPool/src/main/java/ThreadPoolFactory.java new file mode 100644 index 0000000..53ebbd0 --- /dev/null +++ b/ThreadPool/src/main/java/ThreadPoolFactory.java @@ -0,0 +1,26 @@ +import java.util.LinkedList; +import java.util.Queue; + +public class ThreadPoolFactory { + + public static ThreadPool initThreadPool(final int threadsNumber) { + return new ThreadPoolImpl(threadsNumber); + } + + private static class ThreadPoolImpl implements ThreadPool { + + private final int threadsNumber; + + private final Queue tasks = new LinkedList<>(); + + private ThreadPoolImpl(final int threadsNumber) { + this.threadsNumber = threadsNumber; + } + + @Override + public void addTask(final Runnable task) { + + } + + } +} From 8da4fa70af2f00b637c64e955a1ae3eabd86190f Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Wed, 28 Feb 2018 21:55:33 +0300 Subject: [PATCH 07/19] threadPool with simple tasks --- ThreadPool/src/main/java/BlockingQueue.java | 27 +++++++ ThreadPool/src/main/java/ThreadPool.java | 3 + .../src/main/java/ThreadPoolFactory.java | 71 ++++++++++++++++++- .../java/ThreadPoolIsTurnedDownException.java | 3 + ThreadPool/src/main/java/Utils.java | 11 +++ .../src/main/java/tasks/LightFuture.java | 11 +++ ThreadPool/src/main/java/tasks/SleepTask.java | 19 +++++ ThreadPool/src/main/java/tasks/Task.java | 12 ++++ 8 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 ThreadPool/src/main/java/BlockingQueue.java create mode 100644 ThreadPool/src/main/java/ThreadPoolIsTurnedDownException.java create mode 100644 ThreadPool/src/main/java/Utils.java create mode 100644 ThreadPool/src/main/java/tasks/LightFuture.java create mode 100644 ThreadPool/src/main/java/tasks/SleepTask.java create mode 100644 ThreadPool/src/main/java/tasks/Task.java diff --git a/ThreadPool/src/main/java/BlockingQueue.java b/ThreadPool/src/main/java/BlockingQueue.java new file mode 100644 index 0000000..036d0c6 --- /dev/null +++ b/ThreadPool/src/main/java/BlockingQueue.java @@ -0,0 +1,27 @@ +import java.util.LinkedList; +import java.util.Queue; + +public class BlockingQueue { + + private final Queue tasks = new LinkedList<>(); + + public void push(final T task) { + synchronized (tasks) { + tasks.add(task); + tasks.notify(); + } + } + + public T pop() { + synchronized (tasks) { + while (tasks.isEmpty()) { + try { + tasks.wait(); + } catch (final InterruptedException e) { + // do nothing + } + } + return tasks.poll(); + } + } +} \ No newline at end of file diff --git a/ThreadPool/src/main/java/ThreadPool.java b/ThreadPool/src/main/java/ThreadPool.java index e6c4fbe..1e3011c 100644 --- a/ThreadPool/src/main/java/ThreadPool.java +++ b/ThreadPool/src/main/java/ThreadPool.java @@ -1,5 +1,8 @@ +import java.util.concurrent.BlockingQueue; + public interface ThreadPool { void addTask(Runnable task); + void shutdown(); } diff --git a/ThreadPool/src/main/java/ThreadPoolFactory.java b/ThreadPool/src/main/java/ThreadPoolFactory.java index 53ebbd0..288abb3 100644 --- a/ThreadPool/src/main/java/ThreadPoolFactory.java +++ b/ThreadPool/src/main/java/ThreadPoolFactory.java @@ -1,5 +1,7 @@ +import tasks.SleepTask; + import java.util.LinkedList; -import java.util.Queue; +import java.util.List; public class ThreadPoolFactory { @@ -9,18 +11,81 @@ public static ThreadPool initThreadPool(final int threadsNumber) { private static class ThreadPoolImpl implements ThreadPool { + private final Runnable POISON_PILL = () -> {}; private final int threadsNumber; - private final Queue tasks = new LinkedList<>(); + private final BlockingQueue tasks = new BlockingQueue<>(); + private final List threads = new LinkedList<>(); + private boolean isWorking = true; + private final int shutdownTimeout = 1000; private ThreadPoolImpl(final int threadsNumber) { this.threadsNumber = threadsNumber; + for (int i = 0; i < threadsNumber; i++) { + threads.add(new Thread(new ThreadPoolTask())); + } + for (final Thread thread : threads) { + thread.start(); + } + } + + @Override + public synchronized void shutdown() { + for (int i = 0; i < threadsNumber; i++) { + addTask(POISON_PILL); + } + isWorking = false; + + final Thread shutDownThread = new Thread(() -> { + final double waitingBegin = System.currentTimeMillis(); + while (System.currentTimeMillis() - waitingBegin < shutdownTimeout) { + try { + Thread.sleep(shutdownTimeout); + } catch (final InterruptedException e) { + //do nothing + } + } + for (final Thread thread : threads) { + thread.interrupt(); + } + }); + shutDownThread.start(); } @Override - public void addTask(final Runnable task) { + public synchronized void addTask(final Runnable task) { + if (!isWorking) { + throw new ThreadPoolIsTurnedDownException(); + } + if (task != POISON_PILL) { + System.out.printf("Adding new task..." + '\n'); + } + tasks.push(task ); + } + private class ThreadPoolTask implements Runnable { + + @Override + public void run() { + System.out.println("Starting " + Thread.currentThread()); + while (true) { + final Runnable task = tasks.pop(); + if (task == POISON_PILL) { + break; + } + task.run(); + } + System.out.println("Stopping " + Thread.currentThread()); + } } + } + public static void main(final String[] args) { + final ThreadPool threadPool = initThreadPool(3); + threadPool.addTask(new SleepTask()); +// threadPool.addTask(new SleepTask()); +// threadPool.addTask(new SleepTask()); +// threadPool.addTask(new SleepTask()); + threadPool.shutdown(); } } diff --git a/ThreadPool/src/main/java/ThreadPoolIsTurnedDownException.java b/ThreadPool/src/main/java/ThreadPoolIsTurnedDownException.java new file mode 100644 index 0000000..9c4f3c5 --- /dev/null +++ b/ThreadPool/src/main/java/ThreadPoolIsTurnedDownException.java @@ -0,0 +1,3 @@ +public class ThreadPoolIsTurnedDownException extends RuntimeException { + +} diff --git a/ThreadPool/src/main/java/Utils.java b/ThreadPool/src/main/java/Utils.java new file mode 100644 index 0000000..bc38720 --- /dev/null +++ b/ThreadPool/src/main/java/Utils.java @@ -0,0 +1,11 @@ +public class Utils { + + public static void sleep(int time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + +} diff --git a/ThreadPool/src/main/java/tasks/LightFuture.java b/ThreadPool/src/main/java/tasks/LightFuture.java new file mode 100644 index 0000000..2c39ced --- /dev/null +++ b/ThreadPool/src/main/java/tasks/LightFuture.java @@ -0,0 +1,11 @@ +package tasks; + +import java.util.function.Supplier; + +public interface LightFuture { + + boolean isReady(); + + T get(); + +} diff --git a/ThreadPool/src/main/java/tasks/SleepTask.java b/ThreadPool/src/main/java/tasks/SleepTask.java new file mode 100644 index 0000000..a92d418 --- /dev/null +++ b/ThreadPool/src/main/java/tasks/SleepTask.java @@ -0,0 +1,19 @@ +package tasks; + +public class SleepTask extends Task { + + private int number = counter; + + @Override + public void run() { + while (true) { + try { + System.out.println("Task " + number + " was started"); + Thread.sleep(10000); + System.out.println("Task " + number + " was finished"); + } catch (InterruptedException e) { + // do nothing + } + } + } +} diff --git a/ThreadPool/src/main/java/tasks/Task.java b/ThreadPool/src/main/java/tasks/Task.java new file mode 100644 index 0000000..60d4f8b --- /dev/null +++ b/ThreadPool/src/main/java/tasks/Task.java @@ -0,0 +1,12 @@ +package tasks; + +@SuppressWarnings("WeakerAccess") +public abstract class Task implements Runnable { + + protected static int counter; + + Task() { + counter++; + } + +} From e4730b98efb611815736597feb19e08f131e8d0c Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Fri, 2 Mar 2018 17:55:19 +0300 Subject: [PATCH 08/19] lightFuture interface and test for many threads --- ThreadPool/src/main/java/ThreadPool.java | 5 +- .../src/main/java/ThreadPoolFactory.java | 42 ++++----------- ThreadPool/src/main/java/Utils.java | 4 +- .../src/main/java/tasks/LightFuture.java | 5 +- ThreadPool/src/main/java/tasks/SleepTask.java | 19 ------- ThreadPool/src/main/java/tasks/Task.java | 23 ++++++-- .../src/test/java/ThreadPoolFactoryTest.java | 52 +++++++++++++++++++ .../test/java/suppliers/NotifySupplier.java | 13 +++++ .../test/java/suppliers/SleepSupplier.java | 18 +++++++ .../src/test/java/suppliers/SupplierBase.java | 14 +++++ .../src/test/java/suppliers/WaitSupplier.java | 19 +++++++ 11 files changed, 151 insertions(+), 63 deletions(-) delete mode 100644 ThreadPool/src/main/java/tasks/SleepTask.java create mode 100644 ThreadPool/src/test/java/ThreadPoolFactoryTest.java create mode 100644 ThreadPool/src/test/java/suppliers/NotifySupplier.java create mode 100644 ThreadPool/src/test/java/suppliers/SleepSupplier.java create mode 100644 ThreadPool/src/test/java/suppliers/SupplierBase.java create mode 100644 ThreadPool/src/test/java/suppliers/WaitSupplier.java diff --git a/ThreadPool/src/main/java/ThreadPool.java b/ThreadPool/src/main/java/ThreadPool.java index 1e3011c..30911b1 100644 --- a/ThreadPool/src/main/java/ThreadPool.java +++ b/ThreadPool/src/main/java/ThreadPool.java @@ -1,8 +1,11 @@ +import tasks.LightFuture; + import java.util.concurrent.BlockingQueue; +import java.util.function.Supplier; public interface ThreadPool { - void addTask(Runnable task); + LightFuture addTask(Supplier supplier); void shutdown(); } diff --git a/ThreadPool/src/main/java/ThreadPoolFactory.java b/ThreadPool/src/main/java/ThreadPoolFactory.java index 288abb3..eecd6b6 100644 --- a/ThreadPool/src/main/java/ThreadPoolFactory.java +++ b/ThreadPool/src/main/java/ThreadPoolFactory.java @@ -1,8 +1,11 @@ -import tasks.SleepTask; +import tasks.LightFuture; +import tasks.Task; import java.util.LinkedList; import java.util.List; +import java.util.function.Supplier; +@SuppressWarnings("WeakerAccess") public class ThreadPoolFactory { public static ThreadPool initThreadPool(final int threadsNumber) { @@ -11,13 +14,12 @@ public static ThreadPool initThreadPool(final int threadsNumber) { private static class ThreadPoolImpl implements ThreadPool { - private final Runnable POISON_PILL = () -> {}; + private final Supplier POISON_PILL = () -> null; private final int threadsNumber; private final BlockingQueue tasks = new BlockingQueue<>(); private final List threads = new LinkedList<>(); private boolean isWorking = true; - private final int shutdownTimeout = 1000; private ThreadPoolImpl(final int threadsNumber) { this.threadsNumber = threadsNumber; @@ -35,39 +37,22 @@ public synchronized void shutdown() { addTask(POISON_PILL); } isWorking = false; - - final Thread shutDownThread = new Thread(() -> { - final double waitingBegin = System.currentTimeMillis(); - while (System.currentTimeMillis() - waitingBegin < shutdownTimeout) { - try { - Thread.sleep(shutdownTimeout); - } catch (final InterruptedException e) { - //do nothing - } - } - for (final Thread thread : threads) { - thread.interrupt(); - } - }); - shutDownThread.start(); } @Override - public synchronized void addTask(final Runnable task) { + public synchronized LightFuture addTask(final Supplier supplier) { if (!isWorking) { throw new ThreadPoolIsTurnedDownException(); } - if (task != POISON_PILL) { - System.out.printf("Adding new task..." + '\n'); - } - tasks.push(task ); + final LightFuture lightFuture = new Task<>(supplier); + tasks.push(lightFuture); + return lightFuture; } private class ThreadPoolTask implements Runnable { @Override public void run() { - System.out.println("Starting " + Thread.currentThread()); while (true) { final Runnable task = tasks.pop(); if (task == POISON_PILL) { @@ -79,13 +64,4 @@ public void run() { } } } - - public static void main(final String[] args) { - final ThreadPool threadPool = initThreadPool(3); - threadPool.addTask(new SleepTask()); -// threadPool.addTask(new SleepTask()); -// threadPool.addTask(new SleepTask()); -// threadPool.addTask(new SleepTask()); - threadPool.shutdown(); - } } diff --git a/ThreadPool/src/main/java/Utils.java b/ThreadPool/src/main/java/Utils.java index bc38720..448c38f 100644 --- a/ThreadPool/src/main/java/Utils.java +++ b/ThreadPool/src/main/java/Utils.java @@ -1,9 +1,9 @@ public class Utils { - public static void sleep(int time) { + public static void sleep(final int time) { try { Thread.sleep(time); - } catch (InterruptedException e) { + } catch (final InterruptedException e) { e.printStackTrace(); } } diff --git a/ThreadPool/src/main/java/tasks/LightFuture.java b/ThreadPool/src/main/java/tasks/LightFuture.java index 2c39ced..79a8b7e 100644 --- a/ThreadPool/src/main/java/tasks/LightFuture.java +++ b/ThreadPool/src/main/java/tasks/LightFuture.java @@ -2,10 +2,7 @@ import java.util.function.Supplier; -public interface LightFuture { +public interface LightFuture extends Runnable { boolean isReady(); - - T get(); - } diff --git a/ThreadPool/src/main/java/tasks/SleepTask.java b/ThreadPool/src/main/java/tasks/SleepTask.java deleted file mode 100644 index a92d418..0000000 --- a/ThreadPool/src/main/java/tasks/SleepTask.java +++ /dev/null @@ -1,19 +0,0 @@ -package tasks; - -public class SleepTask extends Task { - - private int number = counter; - - @Override - public void run() { - while (true) { - try { - System.out.println("Task " + number + " was started"); - Thread.sleep(10000); - System.out.println("Task " + number + " was finished"); - } catch (InterruptedException e) { - // do nothing - } - } - } -} diff --git a/ThreadPool/src/main/java/tasks/Task.java b/ThreadPool/src/main/java/tasks/Task.java index 60d4f8b..27a002f 100644 --- a/ThreadPool/src/main/java/tasks/Task.java +++ b/ThreadPool/src/main/java/tasks/Task.java @@ -1,12 +1,27 @@ package tasks; +import java.util.function.Supplier; + @SuppressWarnings("WeakerAccess") -public abstract class Task implements Runnable { +public class Task implements LightFuture { + + private final Supplier supplier; - protected static int counter; + private boolean ready; + private T value; - Task() { - counter++; + public Task(final Supplier supplier) { + this.supplier = supplier; } + @Override + public boolean isReady() { + return ready; + } + + @Override + public void run() { + value = supplier.get(); + ready = true; + } } diff --git a/ThreadPool/src/test/java/ThreadPoolFactoryTest.java b/ThreadPool/src/test/java/ThreadPoolFactoryTest.java new file mode 100644 index 0000000..201205b --- /dev/null +++ b/ThreadPool/src/test/java/ThreadPoolFactoryTest.java @@ -0,0 +1,52 @@ +import org.junit.jupiter.api.Test; +import suppliers.NotifySupplier; +import suppliers.SleepSupplier; +import suppliers.WaitSupplier; +import tasks.LightFuture; + +import java.util.LinkedList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class ThreadPoolFactoryTest { + + private final int testsNumber = 10; + private int maxThreadsNumber = 10; + + @Test + void testALotOfThreads() { + for (int i = 0; i < maxThreadsNumber; i++) { + testNThreads(i * i / 2); + } + } + + private void testNThreads(final int threadsNumber) { + final ThreadPool pool = ThreadPoolFactory.initThreadPool(threadsNumber); + + final List> list = new LinkedList<>(); + + for (int i = 0; i < threadsNumber - 1; i++) { + list.add(pool.addTask(new WaitSupplier())); + } + pool.addTask(new SleepSupplier()); + pool.addTask(new NotifySupplier()); + pool.shutdown(); + + Utils.sleep(1000); + + for (final LightFuture lightFuture : list) { + assertTrue(lightFuture.isReady()); + } + } + + public static void main(final String[] args) { + final ThreadPool threadPool = ThreadPoolFactory.initThreadPool(2); + threadPool.addTask(new SleepSupplier()); + threadPool.addTask(new SleepSupplier()); + threadPool.addTask(new SleepSupplier()); + threadPool.addTask(new SleepSupplier()); + threadPool.shutdown(); + } + +} \ No newline at end of file diff --git a/ThreadPool/src/test/java/suppliers/NotifySupplier.java b/ThreadPool/src/test/java/suppliers/NotifySupplier.java new file mode 100644 index 0000000..e4f9ded --- /dev/null +++ b/ThreadPool/src/test/java/suppliers/NotifySupplier.java @@ -0,0 +1,13 @@ +package suppliers; + +public class NotifySupplier extends SupplierBase { + + @Override + public Void get() { + synchronized (commonLock) { + System.out.println("Notifying all!"); + commonLock.notifyAll(); + } + return null; + } +} diff --git a/ThreadPool/src/test/java/suppliers/SleepSupplier.java b/ThreadPool/src/test/java/suppliers/SleepSupplier.java new file mode 100644 index 0000000..c8d6178 --- /dev/null +++ b/ThreadPool/src/test/java/suppliers/SleepSupplier.java @@ -0,0 +1,18 @@ +package suppliers; + +import com.google.common.base.Supplier; + +public class SleepSupplier extends SupplierBase { + + @Override + public Void get() { + try { +// System.out.println("Task " + " was started"); + Thread.sleep(100); +// System.out.println("Task " + " was finished"); + } catch (final InterruptedException e) { + // do nothing + } + return null; + } +} diff --git a/ThreadPool/src/test/java/suppliers/SupplierBase.java b/ThreadPool/src/test/java/suppliers/SupplierBase.java new file mode 100644 index 0000000..2f31b11 --- /dev/null +++ b/ThreadPool/src/test/java/suppliers/SupplierBase.java @@ -0,0 +1,14 @@ +package suppliers; + +import java.util.function.Supplier; + +abstract class SupplierBase implements Supplier { + + protected static int counter; + protected static final Object commonLock = new Object(); + + SupplierBase() { + counter++; + } + +} diff --git a/ThreadPool/src/test/java/suppliers/WaitSupplier.java b/ThreadPool/src/test/java/suppliers/WaitSupplier.java new file mode 100644 index 0000000..cbb141d --- /dev/null +++ b/ThreadPool/src/test/java/suppliers/WaitSupplier.java @@ -0,0 +1,19 @@ +package suppliers; + +import java.util.function.Supplier; + +public class WaitSupplier extends SupplierBase { + + @Override + public Void get() { + synchronized (commonLock) { + try { + commonLock.wait(); + } catch (final InterruptedException e) { + //do nothing + } + } + return null; + } + +} From 20d8c757f878ab2eac2883aa672fd50d15036395 Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Sun, 4 Mar 2018 20:42:58 +0300 Subject: [PATCH 09/19] Get methods for Task class. Shutdown method of ThreadPool is concurrent safe now. --- ThreadPool/build.gradle | 14 +--- ThreadPool/gradle/wrapper/gradle-wrapper.jar | Bin 54329 -> 54329 bytes .../gradle/wrapper/gradle-wrapper.properties | 4 +- ThreadPool/src/main/java/ThreadPool.java | 11 --- .../src/main/java/tasks/LightFuture.java | 8 --- ThreadPool/src/main/java/tasks/Task.java | 27 -------- .../java/{ => threadPool}/BlockingQueue.java | 3 + .../src/main/java/threadPool/LightFuture.java | 9 +++ ThreadPool/src/main/java/threadPool/Task.java | 63 ++++++++++++++++++ .../{ => threadPool}/ThreadPoolFactory.java | 23 ++++--- .../exceptions/LightExecutionException.java | 13 ++++ .../ThreadPoolIsTurnedDownException.java | 3 +- .../src/test/java/ThreadPoolFactoryTest.java | 16 ++--- 13 files changed, 112 insertions(+), 82 deletions(-) delete mode 100644 ThreadPool/src/main/java/ThreadPool.java delete mode 100644 ThreadPool/src/main/java/tasks/LightFuture.java delete mode 100644 ThreadPool/src/main/java/tasks/Task.java rename ThreadPool/src/main/java/{ => threadPool}/BlockingQueue.java (91%) create mode 100644 ThreadPool/src/main/java/threadPool/LightFuture.java create mode 100644 ThreadPool/src/main/java/threadPool/Task.java rename ThreadPool/src/main/java/{ => threadPool}/ThreadPoolFactory.java (72%) create mode 100644 ThreadPool/src/main/java/threadPool/exceptions/LightExecutionException.java rename ThreadPool/src/main/java/{ => threadPool/exceptions}/ThreadPoolIsTurnedDownException.java (70%) diff --git a/ThreadPool/build.gradle b/ThreadPool/build.gradle index d730946..80a163a 100644 --- a/ThreadPool/build.gradle +++ b/ThreadPool/build.gradle @@ -88,17 +88,5 @@ dependencies { task wrapper(type: Wrapper) { description = 'Generates gradlew[.bat] scripts' - gradleVersion = '4.3.1' -} - -apply plugin: 'java' - -sourceCompatibility = 1.8 - -repositories { - mavenCentral() -} - -dependencies { - testCompile group: 'junit', name: 'junit', version: '4.12' + gradleVersion = '4.6' } diff --git a/ThreadPool/gradle/wrapper/gradle-wrapper.jar b/ThreadPool/gradle/wrapper/gradle-wrapper.jar index 01b8bf6b1f99cad9213fc495b33ad5bbab8efd20..a5fe1cb94b9ee5ce57e6113458225bcba12d83e3 100644 GIT binary patch delta 63 zcmdnFf_di(<_YF3Kg`qiPqdC?`&9I?h>>A})W$9U4sx+F1bDM^1n1Aqn!M+bKUmFX P{=@eKSinLbFM9w0VoDpr delta 63 zcmdnFf_di(<_YF39&&6=6RjiJJ`{Z{Vq};gwQ LightFuture addTask(Supplier supplier); - - void shutdown(); -} diff --git a/ThreadPool/src/main/java/tasks/LightFuture.java b/ThreadPool/src/main/java/tasks/LightFuture.java deleted file mode 100644 index 79a8b7e..0000000 --- a/ThreadPool/src/main/java/tasks/LightFuture.java +++ /dev/null @@ -1,8 +0,0 @@ -package tasks; - -import java.util.function.Supplier; - -public interface LightFuture extends Runnable { - - boolean isReady(); -} diff --git a/ThreadPool/src/main/java/tasks/Task.java b/ThreadPool/src/main/java/tasks/Task.java deleted file mode 100644 index 27a002f..0000000 --- a/ThreadPool/src/main/java/tasks/Task.java +++ /dev/null @@ -1,27 +0,0 @@ -package tasks; - -import java.util.function.Supplier; - -@SuppressWarnings("WeakerAccess") -public class Task implements LightFuture { - - private final Supplier supplier; - - private boolean ready; - private T value; - - public Task(final Supplier supplier) { - this.supplier = supplier; - } - - @Override - public boolean isReady() { - return ready; - } - - @Override - public void run() { - value = supplier.get(); - ready = true; - } -} diff --git a/ThreadPool/src/main/java/BlockingQueue.java b/ThreadPool/src/main/java/threadPool/BlockingQueue.java similarity index 91% rename from ThreadPool/src/main/java/BlockingQueue.java rename to ThreadPool/src/main/java/threadPool/BlockingQueue.java index 036d0c6..4e239e2 100644 --- a/ThreadPool/src/main/java/BlockingQueue.java +++ b/ThreadPool/src/main/java/threadPool/BlockingQueue.java @@ -1,6 +1,9 @@ +package threadPool; + import java.util.LinkedList; import java.util.Queue; +@SuppressWarnings("WeakerAccess") public class BlockingQueue { private final Queue tasks = new LinkedList<>(); diff --git a/ThreadPool/src/main/java/threadPool/LightFuture.java b/ThreadPool/src/main/java/threadPool/LightFuture.java new file mode 100644 index 0000000..2f482a7 --- /dev/null +++ b/ThreadPool/src/main/java/threadPool/LightFuture.java @@ -0,0 +1,9 @@ +package threadPool; + +import java.util.function.Supplier; + +public interface LightFuture extends Supplier { + + boolean isReady(); + +} diff --git a/ThreadPool/src/main/java/threadPool/Task.java b/ThreadPool/src/main/java/threadPool/Task.java new file mode 100644 index 0000000..0a9ec1d --- /dev/null +++ b/ThreadPool/src/main/java/threadPool/Task.java @@ -0,0 +1,63 @@ +package threadPool; + +import threadPool.exceptions.LightExecutionException; + +import java.util.function.Supplier; + +/** + * Simple LightFuture interface implementation, + * + * @param return type of execution + */ +// This class should not be seen by anyone from outside, in addition, run method +// is not expected to be executed in multi-thread environment. +class Task implements LightFuture, Runnable { + + private final Supplier supplier; + + private boolean ready; + private T value; + + private Exception error; + + Task(final Supplier supplier) { + this.supplier = supplier; + } + + @Override + public boolean isReady() { + return ready; + } + + @Override + public T get() { + while (!ready) { // waiting for result + try { + wait(); + } catch (final InterruptedException e) { + // nothing to do here + } + } + if (error != null) { + throw new LightExecutionException(error); + } + return value; + } + + + + @Override + public synchronized void run() { + if (!ready) { // we may execute every task only one time + try { + value = supplier.get(); + } catch (final Exception e) { + error = e; + } + ready = true; + notifyAll(); + } else { + throw new RuntimeException(); // should never happen, cause it is an inner method + } + } +} diff --git a/ThreadPool/src/main/java/ThreadPoolFactory.java b/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java similarity index 72% rename from ThreadPool/src/main/java/ThreadPoolFactory.java rename to ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java index eecd6b6..0645a6e 100644 --- a/ThreadPool/src/main/java/ThreadPoolFactory.java +++ b/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java @@ -1,5 +1,6 @@ -import tasks.LightFuture; -import tasks.Task; +package threadPool; + +import threadPool.exceptions.ThreadPoolIsTurnedDownException; import java.util.LinkedList; import java.util.List; @@ -24,7 +25,7 @@ private static class ThreadPoolImpl implements ThreadPool { private ThreadPoolImpl(final int threadsNumber) { this.threadsNumber = threadsNumber; for (int i = 0; i < threadsNumber; i++) { - threads.add(new Thread(new ThreadPoolTask())); + threads.add(new Thread(new ThreadTask())); } for (final Thread thread : threads) { thread.start(); @@ -33,8 +34,10 @@ private ThreadPoolImpl(final int threadsNumber) { @Override public synchronized void shutdown() { - for (int i = 0; i < threadsNumber; i++) { - addTask(POISON_PILL); + if (isWorking) { // otherwise, we may do this section twice + for (int i = 0; i < threadsNumber; i++) { + addTask(POISON_PILL); + } } isWorking = false; } @@ -44,12 +47,14 @@ public synchronized LightFuture addTask(final Supplier supplier) { if (!isWorking) { throw new ThreadPoolIsTurnedDownException(); } - final LightFuture lightFuture = new Task<>(supplier); - tasks.push(lightFuture); - return lightFuture; + final Task task = new Task<>(supplier); + tasks.push(task); + return task; } - private class ThreadPoolTask implements Runnable { + // it is possible to use lambda function instead of this class, + // but constructor would be way to massive in that case. + private class ThreadTask implements Runnable { @Override public void run() { diff --git a/ThreadPool/src/main/java/threadPool/exceptions/LightExecutionException.java b/ThreadPool/src/main/java/threadPool/exceptions/LightExecutionException.java new file mode 100644 index 0000000..3b7fe5b --- /dev/null +++ b/ThreadPool/src/main/java/threadPool/exceptions/LightExecutionException.java @@ -0,0 +1,13 @@ +package threadPool.exceptions; + +public class LightExecutionException extends RuntimeException { + + @SuppressWarnings("unused") + public LightExecutionException() { + super(); + } + + public LightExecutionException(final Exception error) { + super(error); + } +} diff --git a/ThreadPool/src/main/java/ThreadPoolIsTurnedDownException.java b/ThreadPool/src/main/java/threadPool/exceptions/ThreadPoolIsTurnedDownException.java similarity index 70% rename from ThreadPool/src/main/java/ThreadPoolIsTurnedDownException.java rename to ThreadPool/src/main/java/threadPool/exceptions/ThreadPoolIsTurnedDownException.java index 9c4f3c5..9033b57 100644 --- a/ThreadPool/src/main/java/ThreadPoolIsTurnedDownException.java +++ b/ThreadPool/src/main/java/threadPool/exceptions/ThreadPoolIsTurnedDownException.java @@ -1,3 +1,4 @@ -public class ThreadPoolIsTurnedDownException extends RuntimeException { +package threadPool.exceptions; +public class ThreadPoolIsTurnedDownException extends RuntimeException { } diff --git a/ThreadPool/src/test/java/ThreadPoolFactoryTest.java b/ThreadPool/src/test/java/ThreadPoolFactoryTest.java index 201205b..efa1dc4 100644 --- a/ThreadPool/src/test/java/ThreadPoolFactoryTest.java +++ b/ThreadPool/src/test/java/ThreadPoolFactoryTest.java @@ -2,17 +2,20 @@ import suppliers.NotifySupplier; import suppliers.SleepSupplier; import suppliers.WaitSupplier; -import tasks.LightFuture; +import threadPool.LightFuture; +import threadPool.ThreadPool; +import threadPool.ThreadPoolFactory; import java.util.LinkedList; import java.util.List; import static org.junit.jupiter.api.Assertions.*; +@SuppressWarnings("FieldCanBeLocal") class ThreadPoolFactoryTest { private final int testsNumber = 10; - private int maxThreadsNumber = 10; + private final int maxThreadsNumber = 10; @Test void testALotOfThreads() { @@ -40,13 +43,4 @@ private void testNThreads(final int threadsNumber) { } } - public static void main(final String[] args) { - final ThreadPool threadPool = ThreadPoolFactory.initThreadPool(2); - threadPool.addTask(new SleepSupplier()); - threadPool.addTask(new SleepSupplier()); - threadPool.addTask(new SleepSupplier()); - threadPool.addTask(new SleepSupplier()); - threadPool.shutdown(); - } - } \ No newline at end of file From afd794b47e05ee7f74d24b00c469e53a295e6647 Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Sun, 4 Mar 2018 23:22:13 +0300 Subject: [PATCH 10/19] ThreadPool interface was added to git after refactoring. --- ThreadPool/src/main/java/threadPool/ThreadPool.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 ThreadPool/src/main/java/threadPool/ThreadPool.java diff --git a/ThreadPool/src/main/java/threadPool/ThreadPool.java b/ThreadPool/src/main/java/threadPool/ThreadPool.java new file mode 100644 index 0000000..5c9b0f4 --- /dev/null +++ b/ThreadPool/src/main/java/threadPool/ThreadPool.java @@ -0,0 +1,13 @@ +package threadPool; + +import java.util.function.Supplier; + +public interface ThreadPool { + + // in general, we may be able to work with any Task (Runnable interface), + // but here we want to work with LightFuture tasks only to provide user + // convenient interface to track tasks progress e.t.c. + LightFuture addTask(Supplier supplier); + + void shutdown(); +} From 2d3545382da8685f42ab5e36735946ec525cc00f Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Mon, 5 Mar 2018 01:23:49 +0300 Subject: [PATCH 11/19] waitForShutDown method was added to the ThreadPool interface. Simple test for threadPool was added --- ThreadPool/src/main/java/threadPool/Task.java | 2 - .../src/main/java/threadPool/ThreadPool.java | 2 + .../java/threadPool/ThreadPoolFactory.java | 38 ++++++++++- .../src/test/java/ThreadPoolFactoryTest.java | 68 +++++++++++++++++-- .../test/java/suppliers/CounterSupplier.java | 27 ++++++++ .../test/java/suppliers/NotifySupplier.java | 1 - .../test/java/suppliers/SleepSupplier.java | 2 - .../src/test/java/suppliers/SupplierBase.java | 7 +- .../src/test/java/suppliers/WaitSupplier.java | 2 - 9 files changed, 129 insertions(+), 20 deletions(-) create mode 100644 ThreadPool/src/test/java/suppliers/CounterSupplier.java diff --git a/ThreadPool/src/main/java/threadPool/Task.java b/ThreadPool/src/main/java/threadPool/Task.java index 0a9ec1d..897e93a 100644 --- a/ThreadPool/src/main/java/threadPool/Task.java +++ b/ThreadPool/src/main/java/threadPool/Task.java @@ -44,8 +44,6 @@ public T get() { return value; } - - @Override public synchronized void run() { if (!ready) { // we may execute every task only one time diff --git a/ThreadPool/src/main/java/threadPool/ThreadPool.java b/ThreadPool/src/main/java/threadPool/ThreadPool.java index 5c9b0f4..9f11d61 100644 --- a/ThreadPool/src/main/java/threadPool/ThreadPool.java +++ b/ThreadPool/src/main/java/threadPool/ThreadPool.java @@ -10,4 +10,6 @@ public interface ThreadPool { LightFuture addTask(Supplier supplier); void shutdown(); + + void waitWithShutDown(); } diff --git a/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java b/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java index 0645a6e..59393f8 100644 --- a/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java +++ b/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java @@ -16,14 +16,23 @@ public static ThreadPool initThreadPool(final int threadsNumber) { private static class ThreadPoolImpl implements ThreadPool { private final Supplier POISON_PILL = () -> null; - private final int threadsNumber; + private final Task POISON_PILL_TASK = new Task<>(POISON_PILL); + + + // Locks + private final Object shutDownLock = new Object(); private final BlockingQueue tasks = new BlockingQueue<>(); private final List threads = new LinkedList<>(); + + private final int threadsNumber; private boolean isWorking = true; + private int threadsStillWorking; + private ThreadPoolImpl(final int threadsNumber) { this.threadsNumber = threadsNumber; + threadsStillWorking = threadsNumber; for (int i = 0; i < threadsNumber; i++) { threads.add(new Thread(new ThreadTask())); } @@ -42,11 +51,29 @@ public synchronized void shutdown() { isWorking = false; } + @Override + public void waitWithShutDown() { + shutdown(); + synchronized (shutDownLock) { + while (threadsStillWorking != 0) { + try { + shutDownLock.wait(); + } catch (final InterruptedException e) { +// do nothing + } + } + } + } + @Override public synchronized LightFuture addTask(final Supplier supplier) { if (!isWorking) { throw new ThreadPoolIsTurnedDownException(); } + if (supplier == POISON_PILL) { + tasks.push(POISON_PILL_TASK); + return null; + } final Task task = new Task<>(supplier); tasks.push(task); return task; @@ -60,12 +87,17 @@ private class ThreadTask implements Runnable { public void run() { while (true) { final Runnable task = tasks.pop(); - if (task == POISON_PILL) { + if (task == POISON_PILL_TASK) { + synchronized (shutDownLock) { + threadsStillWorking--; + if (threadsStillWorking == 0) { + shutDownLock.notifyAll(); + } + } break; } task.run(); } - System.out.println("Stopping " + Thread.currentThread()); } } } diff --git a/ThreadPool/src/test/java/ThreadPoolFactoryTest.java b/ThreadPool/src/test/java/ThreadPoolFactoryTest.java index efa1dc4..475bfa0 100644 --- a/ThreadPool/src/test/java/ThreadPoolFactoryTest.java +++ b/ThreadPool/src/test/java/ThreadPoolFactoryTest.java @@ -1,4 +1,5 @@ import org.junit.jupiter.api.Test; +import suppliers.CounterSupplier; import suppliers.NotifySupplier; import suppliers.SleepSupplier; import suppliers.WaitSupplier; @@ -14,9 +15,40 @@ @SuppressWarnings("FieldCanBeLocal") class ThreadPoolFactoryTest { - private final int testsNumber = 10; + private final int testsNumber = 100; private final int maxThreadsNumber = 10; + /** + * This test checks if every task was executed exactly one time. + */ + @Test + void testSimple() { + for (int t = 0; t < testsNumber; t++) { + final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); + final List list = new LinkedList<>(); + + for (int i = 0; i < 10; i++) { + final CounterSupplier dummy = new CounterSupplier(); + list.add(dummy); + pool.addTask(dummy); + } + + pool.waitWithShutDown(); + + assertEquals((t + 1) * 10, list.get(0).getCounter()); + for (final CounterSupplier counterSupplier : list) { + assertEquals(1, counterSupplier.getMyCounter()); + } + } + } + + /** + * This test checks if there at least n threads int the pool. + * + * Initialises n - 1 thread with waiting tasks and notifying them with the last one. + * If there are not enough threads in the pool, test will stuck, therefore there is + * a timeout for test execution. + */ @Test void testALotOfThreads() { for (int i = 0; i < maxThreadsNumber; i++) { @@ -25,8 +57,16 @@ void testALotOfThreads() { } private void testNThreads(final int threadsNumber) { - final ThreadPool pool = ThreadPoolFactory.initThreadPool(threadsNumber); + final Thread timer = new Thread(() -> { + Utils.sleep(5000); + synchronized (this) { + notify(); + } + }); + timer.start(); + + final ThreadPool pool = ThreadPoolFactory.initThreadPool(threadsNumber); final List> list = new LinkedList<>(); for (int i = 0; i < threadsNumber - 1; i++) { @@ -34,9 +74,29 @@ private void testNThreads(final int threadsNumber) { } pool.addTask(new SleepSupplier()); pool.addTask(new NotifySupplier()); - pool.shutdown(); - Utils.sleep(1000); + new Thread(() -> { + pool.waitWithShutDown(); + synchronized (this) { + notify(); + } + }).start(); + + synchronized (this) { + try { + // there must be loop, but i cannot change variable + // for it in lambda... What should I do with it? + // Here I will hope that at least one test will fail. + wait(); + } catch (final InterruptedException e) { + // do nothing + } + // Am i right that timer cannot still work? + // In case thread was set on this one immediately? + if (!timer.isAlive()) { + fail("Out of time."); + } + } for (final LightFuture lightFuture : list) { assertTrue(lightFuture.isReady()); diff --git a/ThreadPool/src/test/java/suppliers/CounterSupplier.java b/ThreadPool/src/test/java/suppliers/CounterSupplier.java new file mode 100644 index 0000000..ef08699 --- /dev/null +++ b/ThreadPool/src/test/java/suppliers/CounterSupplier.java @@ -0,0 +1,27 @@ +package suppliers; + +public class CounterSupplier extends SupplierBase { + + private static int counter; + private int myCounter; + + public int getCounter() { + return counter; + } + + @Override + public Void get() { + synchronized (this) { + myCounter++; + } + synchronized (commonLock) { + counter++; + } + return null; + } + + public int getMyCounter() { + return myCounter; + } + +} diff --git a/ThreadPool/src/test/java/suppliers/NotifySupplier.java b/ThreadPool/src/test/java/suppliers/NotifySupplier.java index e4f9ded..615bc7c 100644 --- a/ThreadPool/src/test/java/suppliers/NotifySupplier.java +++ b/ThreadPool/src/test/java/suppliers/NotifySupplier.java @@ -5,7 +5,6 @@ public class NotifySupplier extends SupplierBase { @Override public Void get() { synchronized (commonLock) { - System.out.println("Notifying all!"); commonLock.notifyAll(); } return null; diff --git a/ThreadPool/src/test/java/suppliers/SleepSupplier.java b/ThreadPool/src/test/java/suppliers/SleepSupplier.java index c8d6178..4cbe2f9 100644 --- a/ThreadPool/src/test/java/suppliers/SleepSupplier.java +++ b/ThreadPool/src/test/java/suppliers/SleepSupplier.java @@ -7,9 +7,7 @@ public class SleepSupplier extends SupplierBase { @Override public Void get() { try { -// System.out.println("Task " + " was started"); Thread.sleep(100); -// System.out.println("Task " + " was finished"); } catch (final InterruptedException e) { // do nothing } diff --git a/ThreadPool/src/test/java/suppliers/SupplierBase.java b/ThreadPool/src/test/java/suppliers/SupplierBase.java index 2f31b11..a477bc1 100644 --- a/ThreadPool/src/test/java/suppliers/SupplierBase.java +++ b/ThreadPool/src/test/java/suppliers/SupplierBase.java @@ -4,11 +4,6 @@ abstract class SupplierBase implements Supplier { - protected static int counter; - protected static final Object commonLock = new Object(); - - SupplierBase() { - counter++; - } + static final Object commonLock = new Object(); } diff --git a/ThreadPool/src/test/java/suppliers/WaitSupplier.java b/ThreadPool/src/test/java/suppliers/WaitSupplier.java index cbb141d..5f2f9c0 100644 --- a/ThreadPool/src/test/java/suppliers/WaitSupplier.java +++ b/ThreadPool/src/test/java/suppliers/WaitSupplier.java @@ -1,7 +1,5 @@ package suppliers; -import java.util.function.Supplier; - public class WaitSupplier extends SupplierBase { @Override From 9cff4622ce226cb166c3b616baf30b5e77139e30 Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Mon, 5 Mar 2018 01:48:02 +0300 Subject: [PATCH 12/19] Some docs --- .../main/java/threadPool/BlockingQueue.java | 24 +++++++++++- .../src/main/java/threadPool/ThreadPool.java | 38 +++++++++++++++++-- .../java/threadPool/ThreadPoolFactory.java | 19 +++++++++- .../exceptions/LightExecutionException.java | 4 ++ .../ThreadPoolIsTurnedDownException.java | 4 ++ 5 files changed, 82 insertions(+), 7 deletions(-) diff --git a/ThreadPool/src/main/java/threadPool/BlockingQueue.java b/ThreadPool/src/main/java/threadPool/BlockingQueue.java index 4e239e2..5f0579f 100644 --- a/ThreadPool/src/main/java/threadPool/BlockingQueue.java +++ b/ThreadPool/src/main/java/threadPool/BlockingQueue.java @@ -3,18 +3,38 @@ import java.util.LinkedList; import java.util.Queue; +/** + * This class provides safe concurrent queue. + * + * In other words, this class is a wrapper around simple queue class, + * but in addition every action in concurrent program will be safe, that means, + * that you are not able to override any values in your queue, or to take + * the same value twice. + * + * @param type of the queue. + */ @SuppressWarnings("WeakerAccess") public class BlockingQueue { private final Queue tasks = new LinkedList<>(); - public void push(final T task) { + /** + * Adds new item to the end of the queue. + * + * @param item item to add into queue. + */ + public void push(final T item) { synchronized (tasks) { - tasks.add(task); + tasks.add(item); tasks.notify(); } } + /** + * The method extracts top item from the queue. + * + * @return top item of the queue. + */ public T pop() { synchronized (tasks) { while (tasks.isEmpty()) { diff --git a/ThreadPool/src/main/java/threadPool/ThreadPool.java b/ThreadPool/src/main/java/threadPool/ThreadPool.java index 9f11d61..2948fc2 100644 --- a/ThreadPool/src/main/java/threadPool/ThreadPool.java +++ b/ThreadPool/src/main/java/threadPool/ThreadPool.java @@ -2,14 +2,46 @@ import java.util.function.Supplier; +// In general, we may be able to work with any Task (Runnable interface), +// but here we want to work with LightFuture tasks only to provide user +// convenient interface to track tasks progress e.t.c. + +/** + * This interface provides base thread pool interface, which means that you are + * able to add new tasks to the pool and to call shutdown method, which turns the pool off. + * + * In addition, the pool provides convenient interface to track your tasks progress + * with the LightFuture interface. Every task will be wrapped in this interface. + */ public interface ThreadPool { - // in general, we may be able to work with any Task (Runnable interface), - // but here we want to work with LightFuture tasks only to provide user - // convenient interface to track tasks progress e.t.c. + /** + * The method adds new task to the thread pool. Added tasks is equals to one "get" + * calculation of the supplier. + * + * In case of trying to add new task after shutdown ThreadPoolIsTurnedDownException + * will be thrown. + * + * @param supplier supplier to create task from. + * @param type of the result of calculation. + * @return new LightFuture object to track your task state. + */ LightFuture addTask(Supplier supplier); + /** + * Tells pool to shutdown. After calling this method no more tasks + * will be applied to the pool. All tasks, that are already stored + * will be finished. + * + * In case of trying to add new task after shutdown ThreadPoolIsTurnedDownException + * will be thrown. + */ void shutdown(); + /** + * This method stops your main thread until all tasks in poll will be executed + * and calls shutdown method of the pool, therefore after calling this method + * you might be sure that all tasks added to the pool are already finished. + */ void waitWithShutDown(); } diff --git a/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java b/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java index 59393f8..8f068c0 100644 --- a/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java +++ b/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java @@ -6,6 +6,9 @@ import java.util.List; import java.util.function.Supplier; +/** + * + */ @SuppressWarnings("WeakerAccess") public class ThreadPoolFactory { @@ -41,6 +44,9 @@ private ThreadPoolImpl(final int threadsNumber) { } } + /** + * {@link ThreadPool#shutdown()} + */ @Override public synchronized void shutdown() { if (isWorking) { // otherwise, we may do this section twice @@ -51,6 +57,9 @@ public synchronized void shutdown() { isWorking = false; } + /** + * {@link ThreadPool#waitWithShutDown()} + */ @Override public void waitWithShutDown() { shutdown(); @@ -65,6 +74,9 @@ public void waitWithShutDown() { } } + /** + * {@link ThreadPool#addTask(Supplier)} + */ @Override public synchronized LightFuture addTask(final Supplier supplier) { if (!isWorking) { @@ -79,8 +91,11 @@ public synchronized LightFuture addTask(final Supplier supplier) { return task; } - // it is possible to use lambda function instead of this class, - // but constructor would be way to massive in that case. + /** + * Default task for every thread in the thread pool. + * + * Taking tasks from the queue until thread pool is working. + */ private class ThreadTask implements Runnable { @Override diff --git a/ThreadPool/src/main/java/threadPool/exceptions/LightExecutionException.java b/ThreadPool/src/main/java/threadPool/exceptions/LightExecutionException.java index 3b7fe5b..f2d0908 100644 --- a/ThreadPool/src/main/java/threadPool/exceptions/LightExecutionException.java +++ b/ThreadPool/src/main/java/threadPool/exceptions/LightExecutionException.java @@ -1,5 +1,9 @@ package threadPool.exceptions; +/** + * This class provides exception to be thrown in case of calling get method + * of calculation, which was failed with th exception. + */ public class LightExecutionException extends RuntimeException { @SuppressWarnings("unused") diff --git a/ThreadPool/src/main/java/threadPool/exceptions/ThreadPoolIsTurnedDownException.java b/ThreadPool/src/main/java/threadPool/exceptions/ThreadPoolIsTurnedDownException.java index 9033b57..31eb2f0 100644 --- a/ThreadPool/src/main/java/threadPool/exceptions/ThreadPoolIsTurnedDownException.java +++ b/ThreadPool/src/main/java/threadPool/exceptions/ThreadPoolIsTurnedDownException.java @@ -1,4 +1,8 @@ package threadPool.exceptions; +/** + * This exception is thrown in case of trying to add new task in the pool, that + * was already turned off. + */ public class ThreadPoolIsTurnedDownException extends RuntimeException { } From d3ef4156c3bc78f025c79d5c660209968239fea9 Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Wed, 7 Mar 2018 01:09:33 +0300 Subject: [PATCH 13/19] thenApply method and some docs --- .../src/main/java/threadPool/LightFuture.java | 14 ++ ThreadPool/src/main/java/threadPool/Task.java | 61 ----- .../java/threadPool/ThreadPoolFactory.java | 101 -------- .../main/java/threadPool/ThreadPoolImpl.java | 229 ++++++++++++++++++ .../TaskIsReadyAlreadException.java | 5 + ThreadPool/src/{main => test}/java/Utils.java | 0 6 files changed, 248 insertions(+), 162 deletions(-) delete mode 100644 ThreadPool/src/main/java/threadPool/Task.java create mode 100644 ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java create mode 100644 ThreadPool/src/main/java/threadPool/exceptions/TaskIsReadyAlreadException.java rename ThreadPool/src/{main => test}/java/Utils.java (100%) diff --git a/ThreadPool/src/main/java/threadPool/LightFuture.java b/ThreadPool/src/main/java/threadPool/LightFuture.java index 2f482a7..f310e71 100644 --- a/ThreadPool/src/main/java/threadPool/LightFuture.java +++ b/ThreadPool/src/main/java/threadPool/LightFuture.java @@ -1,9 +1,23 @@ package threadPool; +import java.util.function.Function; import java.util.function.Supplier; public interface LightFuture extends Supplier { boolean isReady(); + /** + * The method adds new task to the threadPool, which depends on the result of + * the calculation. That means, that task cannot be evaluated until its parent task + * is not ready. + * + * The method takes function f, and applies it to the result of the calculation. + * + * @param f function to apply + * @param type of the function result + * @return new LightFuture task, which was already added to the pool. + */ + LightFuture thenApply(Function f); + } diff --git a/ThreadPool/src/main/java/threadPool/Task.java b/ThreadPool/src/main/java/threadPool/Task.java deleted file mode 100644 index 897e93a..0000000 --- a/ThreadPool/src/main/java/threadPool/Task.java +++ /dev/null @@ -1,61 +0,0 @@ -package threadPool; - -import threadPool.exceptions.LightExecutionException; - -import java.util.function.Supplier; - -/** - * Simple LightFuture interface implementation, - * - * @param return type of execution - */ -// This class should not be seen by anyone from outside, in addition, run method -// is not expected to be executed in multi-thread environment. -class Task implements LightFuture, Runnable { - - private final Supplier supplier; - - private boolean ready; - private T value; - - private Exception error; - - Task(final Supplier supplier) { - this.supplier = supplier; - } - - @Override - public boolean isReady() { - return ready; - } - - @Override - public T get() { - while (!ready) { // waiting for result - try { - wait(); - } catch (final InterruptedException e) { - // nothing to do here - } - } - if (error != null) { - throw new LightExecutionException(error); - } - return value; - } - - @Override - public synchronized void run() { - if (!ready) { // we may execute every task only one time - try { - value = supplier.get(); - } catch (final Exception e) { - error = e; - } - ready = true; - notifyAll(); - } else { - throw new RuntimeException(); // should never happen, cause it is an inner method - } - } -} diff --git a/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java b/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java index 8f068c0..78a6a21 100644 --- a/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java +++ b/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java @@ -15,105 +15,4 @@ public class ThreadPoolFactory { public static ThreadPool initThreadPool(final int threadsNumber) { return new ThreadPoolImpl(threadsNumber); } - - private static class ThreadPoolImpl implements ThreadPool { - - private final Supplier POISON_PILL = () -> null; - private final Task POISON_PILL_TASK = new Task<>(POISON_PILL); - - - // Locks - private final Object shutDownLock = new Object(); - - private final BlockingQueue tasks = new BlockingQueue<>(); - private final List threads = new LinkedList<>(); - - private final int threadsNumber; - private boolean isWorking = true; - private int threadsStillWorking; - - - private ThreadPoolImpl(final int threadsNumber) { - this.threadsNumber = threadsNumber; - threadsStillWorking = threadsNumber; - for (int i = 0; i < threadsNumber; i++) { - threads.add(new Thread(new ThreadTask())); - } - for (final Thread thread : threads) { - thread.start(); - } - } - - /** - * {@link ThreadPool#shutdown()} - */ - @Override - public synchronized void shutdown() { - if (isWorking) { // otherwise, we may do this section twice - for (int i = 0; i < threadsNumber; i++) { - addTask(POISON_PILL); - } - } - isWorking = false; - } - - /** - * {@link ThreadPool#waitWithShutDown()} - */ - @Override - public void waitWithShutDown() { - shutdown(); - synchronized (shutDownLock) { - while (threadsStillWorking != 0) { - try { - shutDownLock.wait(); - } catch (final InterruptedException e) { -// do nothing - } - } - } - } - - /** - * {@link ThreadPool#addTask(Supplier)} - */ - @Override - public synchronized LightFuture addTask(final Supplier supplier) { - if (!isWorking) { - throw new ThreadPoolIsTurnedDownException(); - } - if (supplier == POISON_PILL) { - tasks.push(POISON_PILL_TASK); - return null; - } - final Task task = new Task<>(supplier); - tasks.push(task); - return task; - } - - /** - * Default task for every thread in the thread pool. - * - * Taking tasks from the queue until thread pool is working. - */ - private class ThreadTask implements Runnable { - - @Override - public void run() { - while (true) { - final Runnable task = tasks.pop(); - if (task == POISON_PILL_TASK) { - synchronized (shutDownLock) { - threadsStillWorking--; - if (threadsStillWorking == 0) { - shutDownLock.notifyAll(); - } - } - break; - } - task.run(); - } - } - } - } } diff --git a/ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java b/ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java new file mode 100644 index 0000000..7337882 --- /dev/null +++ b/ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java @@ -0,0 +1,229 @@ +package threadPool; + +import threadPool.exceptions.LightExecutionException; +import threadPool.exceptions.TaskIsReadyAlreadException; +import threadPool.exceptions.ThreadPoolIsTurnedDownException; + +import java.util.LinkedList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * {@link ThreadPool} + * + * Basic ThreadPool interface implementation. + * + * This class supports safe work in concurrent environment. Class is not visible outside + * from the package, therefore it is possible to work with only with public methods + * of {@link ThreadPool} interface. + * + * In general, this pool supports only tasks, which implements LightFuture interface, + * but there is an easy way to work with any Runnable tasks, therefore blocking queue + * inside of the class stores any kind of Runnable classes. + */ +class ThreadPoolImpl implements ThreadPool { + + private final Supplier POISON_PILL = () -> null; + private final Task POISON_PILL_TASK = new Task<>(POISON_PILL); + + // Locks + private final Object shutDownLock = new Object(); + + private final BlockingQueue tasks = new BlockingQueue<>(); + + private final int threadsNumber; + private boolean isWorking = true; + private int threadsStillWorking; + + + ThreadPoolImpl(final int threadsNumber) { + this.threadsNumber = threadsNumber; + threadsStillWorking = threadsNumber; + final List threads = new LinkedList<>(); + for (int i = 0; i < threadsNumber; i++) { + threads.add(new Thread(new ThreadTask())); + } + for (final Thread thread : threads) { + thread.start(); + } + } + + /** + * Poison pill pattern is used for {@link ThreadPool#shutdown()} implementation. + */ + @Override + public synchronized void shutdown() { + if (isWorking) { // otherwise, we may do this section twice + for (int i = 0; i < threadsNumber; i++) { + addTask(POISON_PILL); + } + } + isWorking = false; + } + + /** + * {@link ThreadPool#waitWithShutDown()} + */ + @Override + public void waitWithShutDown() { + shutdown(); + synchronized (shutDownLock) { + while (threadsStillWorking != 0) { + try { + shutDownLock.wait(); + } catch (final InterruptedException e) { +// do nothing + } + } + } + } + + /** + * {@link ThreadPool#addTask(Supplier)} + */ + @Override + public synchronized LightFuture addTask(final Supplier supplier) { + if (!isWorking) { + throw new ThreadPoolIsTurnedDownException(); + } + if (supplier == POISON_PILL) { + tasks.push(POISON_PILL_TASK); + return null; + } + final Task task = new Task<>(supplier); + tasks.push(task); + return task; + } + + /** + * Default task for every thread in the thread pool. + * + * Taking tasks from the queue until thread pool is working. + */ + private class ThreadTask implements Runnable { + + @Override + public void run() { + while (true) { + final Runnable task = tasks.pop(); + if (task == POISON_PILL_TASK) { + synchronized (shutDownLock) { + threadsStillWorking--; + if (threadsStillWorking == 0) { + shutDownLock.notifyAll(); + } + } + break; + } + task.run(); + } + } + } + + /** + * Simple LightFuture interface implementation, that may be used inside + * ThreadPoolImpl class. This class is not visible from outside, therefore you are + * not able to work with this class directly. + * + * Public interface of this class is safe in concurrent environment. + * + * Talking about not visible for user interface (such as method run, that is not + * visible with the interface LightFuture) it is expected, that programmer + * works safe with this methods (for example, calls run method twice). If there was + * any mistake in the code, TaskIsReadyAlreadyException is expected to be thrown. + * + * If an exception occurred during the task execution this exception will be stored + * inside of the class and passed to every future calculations (thenApply method). + * In case of trying to get result of the failed calculation or execute any + * corresponded on the answer task LightExecutionException will be thrown. + * + * @param return type of execution + */ + private class Task implements LightFuture, Runnable { + + private final Supplier supplier; + + private boolean ready; + private T value; + private Exception error; + + private Task(final Supplier supplier) { + this.supplier = supplier; + } + + /** + * Task is ready if it execution was ended by any thread. It does not matter + * either it was successful or not (ended with an exception). + * + * {@link LightFuture#isReady()} + * + * @return true if task was already completed and false otherwise. + */ + @Override + public boolean isReady() { + return ready; + } + + /** + * Returns the value of the calculation. If calculation is not ready yet, waits + * for its end and then returns the result. + * + * If there was an error during execution LightExecutionException with + * cause of the fail inside will be thrown. + * + * {@link LightFuture#get()} + * + * @return result of the calculation. + */ + @Override + public T get() { + synchronized (this) { + while (!ready) { // waiting for result + try { + wait(); + } catch (final InterruptedException e) { + // nothing to do here + } + } + } + if (error != null) { + throw new LightExecutionException(error); + } + return value; + } + + + /** + * {@link LightFuture#thenApply(Function)} + */ + @Override + public LightFuture thenApply(final Function f) { + final Supplier supplier = () -> f.apply(Task.this.get()); + return addTask(supplier); + } + + /** + * Inner method of class. Should not be visible from outside (of course it is + * possible to call this method with cast of this object to Runnable, + * but let us think that it is a user problem). + * + * This method is called, when thread from the pool staring to execute this task. + * Runnable interface is supported for this very reason. + */ + @Override + public synchronized void run() { + if (!ready) { // we may execute every task only one time + try { + value = supplier.get(); + } catch (final Exception e) { + error = e; + } + ready = true; + notifyAll(); + } else { + throw new TaskIsReadyAlreadException(); // should never happen, cause it is an inner method + } + } + } +} diff --git a/ThreadPool/src/main/java/threadPool/exceptions/TaskIsReadyAlreadException.java b/ThreadPool/src/main/java/threadPool/exceptions/TaskIsReadyAlreadException.java new file mode 100644 index 0000000..fc91230 --- /dev/null +++ b/ThreadPool/src/main/java/threadPool/exceptions/TaskIsReadyAlreadException.java @@ -0,0 +1,5 @@ +package threadPool.exceptions; + +public class TaskIsReadyAlreadException extends RuntimeException { + +} diff --git a/ThreadPool/src/main/java/Utils.java b/ThreadPool/src/test/java/Utils.java similarity index 100% rename from ThreadPool/src/main/java/Utils.java rename to ThreadPool/src/test/java/Utils.java From 3c78ec780355650bab098a29d604cc15d105784b Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Wed, 7 Mar 2018 01:10:55 +0300 Subject: [PATCH 14/19] reformat code --- ThreadPool/src/main/java/threadPool/LightFuture.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ThreadPool/src/main/java/threadPool/LightFuture.java b/ThreadPool/src/main/java/threadPool/LightFuture.java index f310e71..1fc3801 100644 --- a/ThreadPool/src/main/java/threadPool/LightFuture.java +++ b/ThreadPool/src/main/java/threadPool/LightFuture.java @@ -11,10 +11,10 @@ public interface LightFuture extends Supplier { * The method adds new task to the threadPool, which depends on the result of * the calculation. That means, that task cannot be evaluated until its parent task * is not ready. - * + *

      * The method takes function f, and applies it to the result of the calculation. * - * @param f function to apply + * @param f function to apply * @param type of the function result * @return new LightFuture task, which was already added to the pool. */ From 3f5e17ba9479da2608fa2561ecb5e4f2e0475d3b Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Wed, 7 Mar 2018 14:42:00 +0300 Subject: [PATCH 15/19] Code reformation --- .../main/java/threadPool/BlockingQueue.java | 2 +- .../src/main/java/threadPool/ThreadPool.java | 8 +++---- .../main/java/threadPool/ThreadPoolImpl.java | 24 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ThreadPool/src/main/java/threadPool/BlockingQueue.java b/ThreadPool/src/main/java/threadPool/BlockingQueue.java index 5f0579f..c903ac8 100644 --- a/ThreadPool/src/main/java/threadPool/BlockingQueue.java +++ b/ThreadPool/src/main/java/threadPool/BlockingQueue.java @@ -5,7 +5,7 @@ /** * This class provides safe concurrent queue. - * + *

      * In other words, this class is a wrapper around simple queue class, * but in addition every action in concurrent program will be safe, that means, * that you are not able to override any values in your queue, or to take diff --git a/ThreadPool/src/main/java/threadPool/ThreadPool.java b/ThreadPool/src/main/java/threadPool/ThreadPool.java index 2948fc2..8d08e1d 100644 --- a/ThreadPool/src/main/java/threadPool/ThreadPool.java +++ b/ThreadPool/src/main/java/threadPool/ThreadPool.java @@ -9,7 +9,7 @@ /** * This interface provides base thread pool interface, which means that you are * able to add new tasks to the pool and to call shutdown method, which turns the pool off. - * + *

      * In addition, the pool provides convenient interface to track your tasks progress * with the LightFuture interface. Every task will be wrapped in this interface. */ @@ -18,12 +18,12 @@ public interface ThreadPool { /** * The method adds new task to the thread pool. Added tasks is equals to one "get" * calculation of the supplier. - * + *

      * In case of trying to add new task after shutdown ThreadPoolIsTurnedDownException * will be thrown. * * @param supplier supplier to create task from. - * @param type of the result of calculation. + * @param type of the result of calculation. * @return new LightFuture object to track your task state. */ LightFuture addTask(Supplier supplier); @@ -32,7 +32,7 @@ public interface ThreadPool { * Tells pool to shutdown. After calling this method no more tasks * will be applied to the pool. All tasks, that are already stored * will be finished. - * + *

      * In case of trying to add new task after shutdown ThreadPoolIsTurnedDownException * will be thrown. */ diff --git a/ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java b/ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java index 7337882..f89c48f 100644 --- a/ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java +++ b/ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java @@ -11,13 +11,13 @@ /** * {@link ThreadPool} - * + *

      * Basic ThreadPool interface implementation. - * + *

      * This class supports safe work in concurrent environment. Class is not visible outside * from the package, therefore it is possible to work with only with public methods * of {@link ThreadPool} interface. - * + *

      * In general, this pool supports only tasks, which implements LightFuture interface, * but there is an easy way to work with any Runnable tasks, therefore blocking queue * inside of the class stores any kind of Runnable classes. @@ -73,7 +73,7 @@ public void waitWithShutDown() { try { shutDownLock.wait(); } catch (final InterruptedException e) { -// do nothing + // do nothing } } } @@ -98,7 +98,7 @@ public synchronized LightFuture addTask(final Supplier supplier) { /** * Default task for every thread in the thread pool. - * + *

      * Taking tasks from the queue until thread pool is working. */ private class ThreadTask implements Runnable { @@ -125,14 +125,14 @@ public void run() { * Simple LightFuture interface implementation, that may be used inside * ThreadPoolImpl class. This class is not visible from outside, therefore you are * not able to work with this class directly. - * + *

      * Public interface of this class is safe in concurrent environment. - * + *

      * Talking about not visible for user interface (such as method run, that is not * visible with the interface LightFuture) it is expected, that programmer * works safe with this methods (for example, calls run method twice). If there was * any mistake in the code, TaskIsReadyAlreadyException is expected to be thrown. - * + *

      * If an exception occurred during the task execution this exception will be stored * inside of the class and passed to every future calculations (thenApply method). * In case of trying to get result of the failed calculation or execute any @@ -155,7 +155,7 @@ private Task(final Supplier supplier) { /** * Task is ready if it execution was ended by any thread. It does not matter * either it was successful or not (ended with an exception). - * + *

      * {@link LightFuture#isReady()} * * @return true if task was already completed and false otherwise. @@ -168,10 +168,10 @@ public boolean isReady() { /** * Returns the value of the calculation. If calculation is not ready yet, waits * for its end and then returns the result. - * + *

      * If there was an error during execution LightExecutionException with * cause of the fail inside will be thrown. - * + *

      * {@link LightFuture#get()} * * @return result of the calculation. @@ -207,7 +207,7 @@ public LightFuture thenApply(final Function f) { * Inner method of class. Should not be visible from outside (of course it is * possible to call this method with cast of this object to Runnable, * but let us think that it is a user problem). - * + *

      * This method is called, when thread from the pool staring to execute this task. * Runnable interface is supported for this very reason. */ From deeb223fe4cf118b0f59ba118ba176043842b640 Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Wed, 7 Mar 2018 16:18:59 +0300 Subject: [PATCH 16/19] Docs, tests, clean up and nullable annotations --- .../main/java/threadPool/BlockingQueue.java | 6 +- .../src/main/java/threadPool/LightFuture.java | 16 +++ .../java/threadPool/ThreadPoolFactory.java | 9 +- .../main/java/threadPool/ThreadPoolImpl.java | 19 +++- .../java => main/java/threadPool}/Utils.java | 6 +- .../TaskIsReadyAlreadException.java | 6 + .../src/test/java/ThreadPoolFactoryTest.java | 103 +++++++++++++++++- .../src/test/java/suppliers/BaseSupplier.java | 14 +++ .../test/java/suppliers/CounterSupplier.java | 5 +- .../test/java/suppliers/IntegerSupplier.java | 15 +++ .../test/java/suppliers/NotifySupplier.java | 5 +- .../test/java/suppliers/SleepSupplier.java | 22 ++-- .../src/test/java/suppliers/SupplierBase.java | 9 -- .../src/test/java/suppliers/WaitSupplier.java | 8 +- 14 files changed, 204 insertions(+), 39 deletions(-) rename ThreadPool/src/{test/java => main/java/threadPool}/Utils.java (57%) create mode 100644 ThreadPool/src/test/java/suppliers/BaseSupplier.java create mode 100644 ThreadPool/src/test/java/suppliers/IntegerSupplier.java delete mode 100644 ThreadPool/src/test/java/suppliers/SupplierBase.java diff --git a/ThreadPool/src/main/java/threadPool/BlockingQueue.java b/ThreadPool/src/main/java/threadPool/BlockingQueue.java index c903ac8..d20b9c5 100644 --- a/ThreadPool/src/main/java/threadPool/BlockingQueue.java +++ b/ThreadPool/src/main/java/threadPool/BlockingQueue.java @@ -1,5 +1,8 @@ package threadPool; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + import java.util.LinkedList; import java.util.Queue; @@ -35,12 +38,13 @@ public void push(final T item) { * * @return top item of the queue. */ + @Nullable public T pop() { synchronized (tasks) { while (tasks.isEmpty()) { try { tasks.wait(); - } catch (final InterruptedException e) { + } catch (@NotNull final InterruptedException e) { // do nothing } } diff --git a/ThreadPool/src/main/java/threadPool/LightFuture.java b/ThreadPool/src/main/java/threadPool/LightFuture.java index 1fc3801..e97bfb7 100644 --- a/ThreadPool/src/main/java/threadPool/LightFuture.java +++ b/ThreadPool/src/main/java/threadPool/LightFuture.java @@ -3,8 +3,24 @@ import java.util.function.Function; import java.util.function.Supplier; +/** + * This interface provides methods for convenient tracking of your tasks. + *

      + * For example, you may check if execution of the task already has ended with + * {@link LightFuture#isReady()}, get result of the calculation at any time (task + * may be not finished yet) or add new tasks with dependency on the result of the + * calculation. + * + * @param + */ public interface LightFuture extends Supplier { + /** + * The method cheks if any the task was already executed. Task may end + * with an error, this method still will return true. + * + * @return true if task was finished already and false otherwise. + */ boolean isReady(); /** diff --git a/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java b/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java index 78a6a21..3c805c5 100644 --- a/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java +++ b/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java @@ -1,13 +1,10 @@ package threadPool; -import threadPool.exceptions.ThreadPoolIsTurnedDownException; - -import java.util.LinkedList; -import java.util.List; -import java.util.function.Supplier; - /** + * Class with factory methods to create ThreadPool interface classes. * + * By this class we hide out {@link ThreadPoolImpl} class and show only + * {@link ThreadPool} interface. */ @SuppressWarnings("WeakerAccess") public class ThreadPoolFactory { diff --git a/ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java b/ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java index f89c48f..d49c8e4 100644 --- a/ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java +++ b/ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java @@ -1,5 +1,7 @@ package threadPool; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import threadPool.exceptions.LightExecutionException; import threadPool.exceptions.TaskIsReadyAlreadException; import threadPool.exceptions.ThreadPoolIsTurnedDownException; @@ -24,7 +26,9 @@ */ class ThreadPoolImpl implements ThreadPool { + @Nullable private final Supplier POISON_PILL = () -> null; + @Nullable private final Task POISON_PILL_TASK = new Task<>(POISON_PILL); // Locks @@ -45,6 +49,7 @@ class ThreadPoolImpl implements ThreadPool { threads.add(new Thread(new ThreadTask())); } for (final Thread thread : threads) { + thread.setDaemon(true); thread.start(); } } @@ -72,7 +77,7 @@ public void waitWithShutDown() { while (threadsStillWorking != 0) { try { shutDownLock.wait(); - } catch (final InterruptedException e) { + } catch (@NotNull final InterruptedException e) { // do nothing } } @@ -82,6 +87,7 @@ public void waitWithShutDown() { /** * {@link ThreadPool#addTask(Supplier)} */ + @Nullable @Override public synchronized LightFuture addTask(final Supplier supplier) { if (!isWorking) { @@ -116,7 +122,9 @@ public void run() { } break; } - task.run(); + if (task != null) { // there was a warning, so it is better to check for sure + task.run(); + } } } } @@ -182,7 +190,7 @@ public T get() { while (!ready) { // waiting for result try { wait(); - } catch (final InterruptedException e) { + } catch (@NotNull final InterruptedException e) { // nothing to do here } } @@ -197,8 +205,9 @@ public T get() { /** * {@link LightFuture#thenApply(Function)} */ + @Nullable @Override - public LightFuture thenApply(final Function f) { + public LightFuture thenApply(@NotNull final Function f) { final Supplier supplier = () -> f.apply(Task.this.get()); return addTask(supplier); } @@ -216,7 +225,7 @@ public synchronized void run() { if (!ready) { // we may execute every task only one time try { value = supplier.get(); - } catch (final Exception e) { + } catch (@NotNull final Exception e) { error = e; } ready = true; diff --git a/ThreadPool/src/test/java/Utils.java b/ThreadPool/src/main/java/threadPool/Utils.java similarity index 57% rename from ThreadPool/src/test/java/Utils.java rename to ThreadPool/src/main/java/threadPool/Utils.java index 448c38f..88c2bb3 100644 --- a/ThreadPool/src/test/java/Utils.java +++ b/ThreadPool/src/main/java/threadPool/Utils.java @@ -1,9 +1,13 @@ +package threadPool; + +import org.jetbrains.annotations.NotNull; + public class Utils { public static void sleep(final int time) { try { Thread.sleep(time); - } catch (final InterruptedException e) { + } catch (@NotNull final InterruptedException e) { e.printStackTrace(); } } diff --git a/ThreadPool/src/main/java/threadPool/exceptions/TaskIsReadyAlreadException.java b/ThreadPool/src/main/java/threadPool/exceptions/TaskIsReadyAlreadException.java index fc91230..48c869a 100644 --- a/ThreadPool/src/main/java/threadPool/exceptions/TaskIsReadyAlreadException.java +++ b/ThreadPool/src/main/java/threadPool/exceptions/TaskIsReadyAlreadException.java @@ -1,5 +1,11 @@ package threadPool.exceptions; +/** + * This exception may be thrown if implementation of {@link threadPool.LightFuture} + * interface methods was not concurrent safe. + *

      + * For example, if tasks method "run" was called more then one time. + */ public class TaskIsReadyAlreadException extends RuntimeException { } diff --git a/ThreadPool/src/test/java/ThreadPoolFactoryTest.java b/ThreadPool/src/test/java/ThreadPoolFactoryTest.java index 475bfa0..8cbbc05 100644 --- a/ThreadPool/src/test/java/ThreadPoolFactoryTest.java +++ b/ThreadPool/src/test/java/ThreadPoolFactoryTest.java @@ -1,12 +1,14 @@ +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; -import suppliers.CounterSupplier; -import suppliers.NotifySupplier; -import suppliers.SleepSupplier; -import suppliers.WaitSupplier; +import suppliers.*; import threadPool.LightFuture; import threadPool.ThreadPool; import threadPool.ThreadPoolFactory; +import threadPool.Utils; +import threadPool.exceptions.LightExecutionException; +import threadPool.exceptions.ThreadPoolIsTurnedDownException; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -22,7 +24,7 @@ class ThreadPoolFactoryTest { * This test checks if every task was executed exactly one time. */ @Test - void testSimple() { + void testCallEveryTaskOnce() { for (int t = 0; t < testsNumber; t++) { final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); final List list = new LinkedList<>(); @@ -88,7 +90,7 @@ private void testNThreads(final int threadsNumber) { // for it in lambda... What should I do with it? // Here I will hope that at least one test will fail. wait(); - } catch (final InterruptedException e) { + } catch (@NotNull final InterruptedException e) { // do nothing } // Am i right that timer cannot still work? @@ -103,4 +105,93 @@ private void testNThreads(final int threadsNumber) { } } + @Test + void testIsReady() { + final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); + final LightFuture future = pool.addTask(new SleepSupplier(250)); + pool.addTask(new SleepSupplier()); + + assertFalse(future.isReady()); + new Thread(() -> assertFalse(future.isReady())).start(); + Utils.sleep(500); + assertTrue(future.isReady()); + new Thread(() -> assertTrue(future.isReady())).start(); + } + + @Test + void testGet() { + final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); + + final ArrayList> list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + list.add(pool.addTask(new IntegerSupplier(i * i))); + } + + final Runnable r = () -> { + for (int i = 0; i < 10; i++) { + assertEquals(Integer.valueOf(i * i), list.get(i).get()); + } + }; + new Thread(r).start(); + new Thread(r).start(); + new Thread(r).start(); + r.run(); + } + + @Test + void testShutDown() { + final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); + final ArrayList> list = new ArrayList<>(); + + list.add(pool.addTask(new SleepSupplier())); + list.add(pool.addTask(new SleepSupplier())); + list.add(pool.addTask(new SleepSupplier())); + + pool.shutdown(); + + assertThrows(ThreadPoolIsTurnedDownException.class, + () -> pool.addTask(new IntegerSupplier(1))); + + pool.waitWithShutDown(); + + for (final LightFuture future : list) { + assertTrue(future.isReady()); + } + } + + @Test + void testExceptions() { + final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); + final LightFuture task = pool.addTask(() -> { + throw new RuntimeException(); + }); + + final Runnable check = () -> { + assertThrows(LightExecutionException.class, task::get); + assertThrows(LightExecutionException.class, + () -> task.thenApply(o -> 1).get()); + }; + + check.run(); + new Thread(check).start(); + new Thread(check).start(); + } + + @Test + void testThenApply() { + final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); + + final LightFuture task = pool.addTask(() -> 1); + for (int i = 0; i < 5; i++) { + pool.addTask(new SleepSupplier()); + } + + final Runnable r = () -> assertEquals(Integer.valueOf(2), task.thenApply(n -> n + 1).get()); + r.run(); + new Thread(r).start(); + new Thread(r).start(); + new Thread(r).start(); + + pool.waitWithShutDown(); + } } \ No newline at end of file diff --git a/ThreadPool/src/test/java/suppliers/BaseSupplier.java b/ThreadPool/src/test/java/suppliers/BaseSupplier.java new file mode 100644 index 0000000..1d996a3 --- /dev/null +++ b/ThreadPool/src/test/java/suppliers/BaseSupplier.java @@ -0,0 +1,14 @@ +package suppliers; + +import java.util.function.Supplier; + +/** + * Base class for some simple suppliers, which may be used in tests. + * + * @param type of stored value. + */ +abstract class BaseSupplier implements Supplier { + + static final Object commonLock = new Object(); + +} diff --git a/ThreadPool/src/test/java/suppliers/CounterSupplier.java b/ThreadPool/src/test/java/suppliers/CounterSupplier.java index ef08699..4771169 100644 --- a/ThreadPool/src/test/java/suppliers/CounterSupplier.java +++ b/ThreadPool/src/test/java/suppliers/CounterSupplier.java @@ -1,6 +1,8 @@ package suppliers; -public class CounterSupplier extends SupplierBase { +import org.jetbrains.annotations.Nullable; + +public class CounterSupplier extends BaseSupplier { private static int counter; private int myCounter; @@ -9,6 +11,7 @@ public int getCounter() { return counter; } + @Nullable @Override public Void get() { synchronized (this) { diff --git a/ThreadPool/src/test/java/suppliers/IntegerSupplier.java b/ThreadPool/src/test/java/suppliers/IntegerSupplier.java new file mode 100644 index 0000000..8162691 --- /dev/null +++ b/ThreadPool/src/test/java/suppliers/IntegerSupplier.java @@ -0,0 +1,15 @@ +package suppliers; + +public class IntegerSupplier extends BaseSupplier { + + private final Integer n; + + public IntegerSupplier(final int n) { + this.n = n; + } + + @Override + public Integer get() { + return n; + } +} diff --git a/ThreadPool/src/test/java/suppliers/NotifySupplier.java b/ThreadPool/src/test/java/suppliers/NotifySupplier.java index 615bc7c..52f0b8a 100644 --- a/ThreadPool/src/test/java/suppliers/NotifySupplier.java +++ b/ThreadPool/src/test/java/suppliers/NotifySupplier.java @@ -1,7 +1,10 @@ package suppliers; -public class NotifySupplier extends SupplierBase { +import org.jetbrains.annotations.Nullable; +public class NotifySupplier extends BaseSupplier { + + @Nullable @Override public Void get() { synchronized (commonLock) { diff --git a/ThreadPool/src/test/java/suppliers/SleepSupplier.java b/ThreadPool/src/test/java/suppliers/SleepSupplier.java index 4cbe2f9..5173a2c 100644 --- a/ThreadPool/src/test/java/suppliers/SleepSupplier.java +++ b/ThreadPool/src/test/java/suppliers/SleepSupplier.java @@ -1,16 +1,24 @@ package suppliers; -import com.google.common.base.Supplier; +import org.jetbrains.annotations.Nullable; +import threadPool.Utils; -public class SleepSupplier extends SupplierBase { +public class SleepSupplier extends BaseSupplier { + private int time = 100; + + public SleepSupplier() { + + } + + public SleepSupplier(final int time) { + this.time = time; + } + + @Nullable @Override public Void get() { - try { - Thread.sleep(100); - } catch (final InterruptedException e) { - // do nothing - } + Utils.sleep(time); return null; } } diff --git a/ThreadPool/src/test/java/suppliers/SupplierBase.java b/ThreadPool/src/test/java/suppliers/SupplierBase.java deleted file mode 100644 index a477bc1..0000000 --- a/ThreadPool/src/test/java/suppliers/SupplierBase.java +++ /dev/null @@ -1,9 +0,0 @@ -package suppliers; - -import java.util.function.Supplier; - -abstract class SupplierBase implements Supplier { - - static final Object commonLock = new Object(); - -} diff --git a/ThreadPool/src/test/java/suppliers/WaitSupplier.java b/ThreadPool/src/test/java/suppliers/WaitSupplier.java index 5f2f9c0..237e2d5 100644 --- a/ThreadPool/src/test/java/suppliers/WaitSupplier.java +++ b/ThreadPool/src/test/java/suppliers/WaitSupplier.java @@ -1,13 +1,17 @@ package suppliers; -public class WaitSupplier extends SupplierBase { +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +public class WaitSupplier extends BaseSupplier { + + @Nullable @Override public Void get() { synchronized (commonLock) { try { commonLock.wait(); - } catch (final InterruptedException e) { + } catch (@NotNull final InterruptedException e) { //do nothing } } From 0c950c43eb17ead45887f6733f66e56a7349b284 Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Wed, 7 Mar 2018 16:25:56 +0300 Subject: [PATCH 17/19] All files are moved to packages --- .../main/java/threadPool/BlockingQueue.java | 54 ---- .../src/main/java/threadPool/LightFuture.java | 39 --- .../src/main/java/threadPool/ThreadPool.java | 47 ---- .../java/threadPool/ThreadPoolFactory.java | 15 -- .../main/java/threadPool/ThreadPoolImpl.java | 238 ------------------ .../src/main/java/threadPool/Utils.java | 15 -- .../exceptions/LightExecutionException.java | 17 -- .../TaskIsReadyAlreadException.java | 11 - .../ThreadPoolIsTurnedDownException.java | 8 - .../src/test/java/ThreadPoolFactoryTest.java | 197 --------------- .../src/test/java/suppliers/BaseSupplier.java | 14 -- .../test/java/suppliers/CounterSupplier.java | 30 --- .../test/java/suppliers/IntegerSupplier.java | 15 -- .../test/java/suppliers/NotifySupplier.java | 15 -- .../test/java/suppliers/SleepSupplier.java | 24 -- .../src/test/java/suppliers/WaitSupplier.java | 21 -- 16 files changed, 760 deletions(-) delete mode 100644 ThreadPool/src/main/java/threadPool/BlockingQueue.java delete mode 100644 ThreadPool/src/main/java/threadPool/LightFuture.java delete mode 100644 ThreadPool/src/main/java/threadPool/ThreadPool.java delete mode 100644 ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java delete mode 100644 ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java delete mode 100644 ThreadPool/src/main/java/threadPool/Utils.java delete mode 100644 ThreadPool/src/main/java/threadPool/exceptions/LightExecutionException.java delete mode 100644 ThreadPool/src/main/java/threadPool/exceptions/TaskIsReadyAlreadException.java delete mode 100644 ThreadPool/src/main/java/threadPool/exceptions/ThreadPoolIsTurnedDownException.java delete mode 100644 ThreadPool/src/test/java/ThreadPoolFactoryTest.java delete mode 100644 ThreadPool/src/test/java/suppliers/BaseSupplier.java delete mode 100644 ThreadPool/src/test/java/suppliers/CounterSupplier.java delete mode 100644 ThreadPool/src/test/java/suppliers/IntegerSupplier.java delete mode 100644 ThreadPool/src/test/java/suppliers/NotifySupplier.java delete mode 100644 ThreadPool/src/test/java/suppliers/SleepSupplier.java delete mode 100644 ThreadPool/src/test/java/suppliers/WaitSupplier.java diff --git a/ThreadPool/src/main/java/threadPool/BlockingQueue.java b/ThreadPool/src/main/java/threadPool/BlockingQueue.java deleted file mode 100644 index d20b9c5..0000000 --- a/ThreadPool/src/main/java/threadPool/BlockingQueue.java +++ /dev/null @@ -1,54 +0,0 @@ -package threadPool; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.LinkedList; -import java.util.Queue; - -/** - * This class provides safe concurrent queue. - *

      - * In other words, this class is a wrapper around simple queue class, - * but in addition every action in concurrent program will be safe, that means, - * that you are not able to override any values in your queue, or to take - * the same value twice. - * - * @param type of the queue. - */ -@SuppressWarnings("WeakerAccess") -public class BlockingQueue { - - private final Queue tasks = new LinkedList<>(); - - /** - * Adds new item to the end of the queue. - * - * @param item item to add into queue. - */ - public void push(final T item) { - synchronized (tasks) { - tasks.add(item); - tasks.notify(); - } - } - - /** - * The method extracts top item from the queue. - * - * @return top item of the queue. - */ - @Nullable - public T pop() { - synchronized (tasks) { - while (tasks.isEmpty()) { - try { - tasks.wait(); - } catch (@NotNull final InterruptedException e) { - // do nothing - } - } - return tasks.poll(); - } - } -} \ No newline at end of file diff --git a/ThreadPool/src/main/java/threadPool/LightFuture.java b/ThreadPool/src/main/java/threadPool/LightFuture.java deleted file mode 100644 index e97bfb7..0000000 --- a/ThreadPool/src/main/java/threadPool/LightFuture.java +++ /dev/null @@ -1,39 +0,0 @@ -package threadPool; - -import java.util.function.Function; -import java.util.function.Supplier; - -/** - * This interface provides methods for convenient tracking of your tasks. - *

      - * For example, you may check if execution of the task already has ended with - * {@link LightFuture#isReady()}, get result of the calculation at any time (task - * may be not finished yet) or add new tasks with dependency on the result of the - * calculation. - * - * @param - */ -public interface LightFuture extends Supplier { - - /** - * The method cheks if any the task was already executed. Task may end - * with an error, this method still will return true. - * - * @return true if task was finished already and false otherwise. - */ - boolean isReady(); - - /** - * The method adds new task to the threadPool, which depends on the result of - * the calculation. That means, that task cannot be evaluated until its parent task - * is not ready. - *

      - * The method takes function f, and applies it to the result of the calculation. - * - * @param f function to apply - * @param type of the function result - * @return new LightFuture task, which was already added to the pool. - */ - LightFuture thenApply(Function f); - -} diff --git a/ThreadPool/src/main/java/threadPool/ThreadPool.java b/ThreadPool/src/main/java/threadPool/ThreadPool.java deleted file mode 100644 index 8d08e1d..0000000 --- a/ThreadPool/src/main/java/threadPool/ThreadPool.java +++ /dev/null @@ -1,47 +0,0 @@ -package threadPool; - -import java.util.function.Supplier; - -// In general, we may be able to work with any Task (Runnable interface), -// but here we want to work with LightFuture tasks only to provide user -// convenient interface to track tasks progress e.t.c. - -/** - * This interface provides base thread pool interface, which means that you are - * able to add new tasks to the pool and to call shutdown method, which turns the pool off. - *

      - * In addition, the pool provides convenient interface to track your tasks progress - * with the LightFuture interface. Every task will be wrapped in this interface. - */ -public interface ThreadPool { - - /** - * The method adds new task to the thread pool. Added tasks is equals to one "get" - * calculation of the supplier. - *

      - * In case of trying to add new task after shutdown ThreadPoolIsTurnedDownException - * will be thrown. - * - * @param supplier supplier to create task from. - * @param type of the result of calculation. - * @return new LightFuture object to track your task state. - */ - LightFuture addTask(Supplier supplier); - - /** - * Tells pool to shutdown. After calling this method no more tasks - * will be applied to the pool. All tasks, that are already stored - * will be finished. - *

      - * In case of trying to add new task after shutdown ThreadPoolIsTurnedDownException - * will be thrown. - */ - void shutdown(); - - /** - * This method stops your main thread until all tasks in poll will be executed - * and calls shutdown method of the pool, therefore after calling this method - * you might be sure that all tasks added to the pool are already finished. - */ - void waitWithShutDown(); -} diff --git a/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java b/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java deleted file mode 100644 index 3c805c5..0000000 --- a/ThreadPool/src/main/java/threadPool/ThreadPoolFactory.java +++ /dev/null @@ -1,15 +0,0 @@ -package threadPool; - -/** - * Class with factory methods to create ThreadPool interface classes. - * - * By this class we hide out {@link ThreadPoolImpl} class and show only - * {@link ThreadPool} interface. - */ -@SuppressWarnings("WeakerAccess") -public class ThreadPoolFactory { - - public static ThreadPool initThreadPool(final int threadsNumber) { - return new ThreadPoolImpl(threadsNumber); - } -} diff --git a/ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java b/ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java deleted file mode 100644 index d49c8e4..0000000 --- a/ThreadPool/src/main/java/threadPool/ThreadPoolImpl.java +++ /dev/null @@ -1,238 +0,0 @@ -package threadPool; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import threadPool.exceptions.LightExecutionException; -import threadPool.exceptions.TaskIsReadyAlreadException; -import threadPool.exceptions.ThreadPoolIsTurnedDownException; - -import java.util.LinkedList; -import java.util.List; -import java.util.function.Function; -import java.util.function.Supplier; - -/** - * {@link ThreadPool} - *

      - * Basic ThreadPool interface implementation. - *

      - * This class supports safe work in concurrent environment. Class is not visible outside - * from the package, therefore it is possible to work with only with public methods - * of {@link ThreadPool} interface. - *

      - * In general, this pool supports only tasks, which implements LightFuture interface, - * but there is an easy way to work with any Runnable tasks, therefore blocking queue - * inside of the class stores any kind of Runnable classes. - */ -class ThreadPoolImpl implements ThreadPool { - - @Nullable - private final Supplier POISON_PILL = () -> null; - @Nullable - private final Task POISON_PILL_TASK = new Task<>(POISON_PILL); - - // Locks - private final Object shutDownLock = new Object(); - - private final BlockingQueue tasks = new BlockingQueue<>(); - - private final int threadsNumber; - private boolean isWorking = true; - private int threadsStillWorking; - - - ThreadPoolImpl(final int threadsNumber) { - this.threadsNumber = threadsNumber; - threadsStillWorking = threadsNumber; - final List threads = new LinkedList<>(); - for (int i = 0; i < threadsNumber; i++) { - threads.add(new Thread(new ThreadTask())); - } - for (final Thread thread : threads) { - thread.setDaemon(true); - thread.start(); - } - } - - /** - * Poison pill pattern is used for {@link ThreadPool#shutdown()} implementation. - */ - @Override - public synchronized void shutdown() { - if (isWorking) { // otherwise, we may do this section twice - for (int i = 0; i < threadsNumber; i++) { - addTask(POISON_PILL); - } - } - isWorking = false; - } - - /** - * {@link ThreadPool#waitWithShutDown()} - */ - @Override - public void waitWithShutDown() { - shutdown(); - synchronized (shutDownLock) { - while (threadsStillWorking != 0) { - try { - shutDownLock.wait(); - } catch (@NotNull final InterruptedException e) { - // do nothing - } - } - } - } - - /** - * {@link ThreadPool#addTask(Supplier)} - */ - @Nullable - @Override - public synchronized LightFuture addTask(final Supplier supplier) { - if (!isWorking) { - throw new ThreadPoolIsTurnedDownException(); - } - if (supplier == POISON_PILL) { - tasks.push(POISON_PILL_TASK); - return null; - } - final Task task = new Task<>(supplier); - tasks.push(task); - return task; - } - - /** - * Default task for every thread in the thread pool. - *

      - * Taking tasks from the queue until thread pool is working. - */ - private class ThreadTask implements Runnable { - - @Override - public void run() { - while (true) { - final Runnable task = tasks.pop(); - if (task == POISON_PILL_TASK) { - synchronized (shutDownLock) { - threadsStillWorking--; - if (threadsStillWorking == 0) { - shutDownLock.notifyAll(); - } - } - break; - } - if (task != null) { // there was a warning, so it is better to check for sure - task.run(); - } - } - } - } - - /** - * Simple LightFuture interface implementation, that may be used inside - * ThreadPoolImpl class. This class is not visible from outside, therefore you are - * not able to work with this class directly. - *

      - * Public interface of this class is safe in concurrent environment. - *

      - * Talking about not visible for user interface (such as method run, that is not - * visible with the interface LightFuture) it is expected, that programmer - * works safe with this methods (for example, calls run method twice). If there was - * any mistake in the code, TaskIsReadyAlreadyException is expected to be thrown. - *

      - * If an exception occurred during the task execution this exception will be stored - * inside of the class and passed to every future calculations (thenApply method). - * In case of trying to get result of the failed calculation or execute any - * corresponded on the answer task LightExecutionException will be thrown. - * - * @param return type of execution - */ - private class Task implements LightFuture, Runnable { - - private final Supplier supplier; - - private boolean ready; - private T value; - private Exception error; - - private Task(final Supplier supplier) { - this.supplier = supplier; - } - - /** - * Task is ready if it execution was ended by any thread. It does not matter - * either it was successful or not (ended with an exception). - *

      - * {@link LightFuture#isReady()} - * - * @return true if task was already completed and false otherwise. - */ - @Override - public boolean isReady() { - return ready; - } - - /** - * Returns the value of the calculation. If calculation is not ready yet, waits - * for its end and then returns the result. - *

      - * If there was an error during execution LightExecutionException with - * cause of the fail inside will be thrown. - *

      - * {@link LightFuture#get()} - * - * @return result of the calculation. - */ - @Override - public T get() { - synchronized (this) { - while (!ready) { // waiting for result - try { - wait(); - } catch (@NotNull final InterruptedException e) { - // nothing to do here - } - } - } - if (error != null) { - throw new LightExecutionException(error); - } - return value; - } - - - /** - * {@link LightFuture#thenApply(Function)} - */ - @Nullable - @Override - public LightFuture thenApply(@NotNull final Function f) { - final Supplier supplier = () -> f.apply(Task.this.get()); - return addTask(supplier); - } - - /** - * Inner method of class. Should not be visible from outside (of course it is - * possible to call this method with cast of this object to Runnable, - * but let us think that it is a user problem). - *

      - * This method is called, when thread from the pool staring to execute this task. - * Runnable interface is supported for this very reason. - */ - @Override - public synchronized void run() { - if (!ready) { // we may execute every task only one time - try { - value = supplier.get(); - } catch (@NotNull final Exception e) { - error = e; - } - ready = true; - notifyAll(); - } else { - throw new TaskIsReadyAlreadException(); // should never happen, cause it is an inner method - } - } - } -} diff --git a/ThreadPool/src/main/java/threadPool/Utils.java b/ThreadPool/src/main/java/threadPool/Utils.java deleted file mode 100644 index 88c2bb3..0000000 --- a/ThreadPool/src/main/java/threadPool/Utils.java +++ /dev/null @@ -1,15 +0,0 @@ -package threadPool; - -import org.jetbrains.annotations.NotNull; - -public class Utils { - - public static void sleep(final int time) { - try { - Thread.sleep(time); - } catch (@NotNull final InterruptedException e) { - e.printStackTrace(); - } - } - -} diff --git a/ThreadPool/src/main/java/threadPool/exceptions/LightExecutionException.java b/ThreadPool/src/main/java/threadPool/exceptions/LightExecutionException.java deleted file mode 100644 index f2d0908..0000000 --- a/ThreadPool/src/main/java/threadPool/exceptions/LightExecutionException.java +++ /dev/null @@ -1,17 +0,0 @@ -package threadPool.exceptions; - -/** - * This class provides exception to be thrown in case of calling get method - * of calculation, which was failed with th exception. - */ -public class LightExecutionException extends RuntimeException { - - @SuppressWarnings("unused") - public LightExecutionException() { - super(); - } - - public LightExecutionException(final Exception error) { - super(error); - } -} diff --git a/ThreadPool/src/main/java/threadPool/exceptions/TaskIsReadyAlreadException.java b/ThreadPool/src/main/java/threadPool/exceptions/TaskIsReadyAlreadException.java deleted file mode 100644 index 48c869a..0000000 --- a/ThreadPool/src/main/java/threadPool/exceptions/TaskIsReadyAlreadException.java +++ /dev/null @@ -1,11 +0,0 @@ -package threadPool.exceptions; - -/** - * This exception may be thrown if implementation of {@link threadPool.LightFuture} - * interface methods was not concurrent safe. - *

      - * For example, if tasks method "run" was called more then one time. - */ -public class TaskIsReadyAlreadException extends RuntimeException { - -} diff --git a/ThreadPool/src/main/java/threadPool/exceptions/ThreadPoolIsTurnedDownException.java b/ThreadPool/src/main/java/threadPool/exceptions/ThreadPoolIsTurnedDownException.java deleted file mode 100644 index 31eb2f0..0000000 --- a/ThreadPool/src/main/java/threadPool/exceptions/ThreadPoolIsTurnedDownException.java +++ /dev/null @@ -1,8 +0,0 @@ -package threadPool.exceptions; - -/** - * This exception is thrown in case of trying to add new task in the pool, that - * was already turned off. - */ -public class ThreadPoolIsTurnedDownException extends RuntimeException { -} diff --git a/ThreadPool/src/test/java/ThreadPoolFactoryTest.java b/ThreadPool/src/test/java/ThreadPoolFactoryTest.java deleted file mode 100644 index 8cbbc05..0000000 --- a/ThreadPool/src/test/java/ThreadPoolFactoryTest.java +++ /dev/null @@ -1,197 +0,0 @@ -import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.Test; -import suppliers.*; -import threadPool.LightFuture; -import threadPool.ThreadPool; -import threadPool.ThreadPoolFactory; -import threadPool.Utils; -import threadPool.exceptions.LightExecutionException; -import threadPool.exceptions.ThreadPoolIsTurnedDownException; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - -@SuppressWarnings("FieldCanBeLocal") -class ThreadPoolFactoryTest { - - private final int testsNumber = 100; - private final int maxThreadsNumber = 10; - - /** - * This test checks if every task was executed exactly one time. - */ - @Test - void testCallEveryTaskOnce() { - for (int t = 0; t < testsNumber; t++) { - final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); - final List list = new LinkedList<>(); - - for (int i = 0; i < 10; i++) { - final CounterSupplier dummy = new CounterSupplier(); - list.add(dummy); - pool.addTask(dummy); - } - - pool.waitWithShutDown(); - - assertEquals((t + 1) * 10, list.get(0).getCounter()); - for (final CounterSupplier counterSupplier : list) { - assertEquals(1, counterSupplier.getMyCounter()); - } - } - } - - /** - * This test checks if there at least n threads int the pool. - * - * Initialises n - 1 thread with waiting tasks and notifying them with the last one. - * If there are not enough threads in the pool, test will stuck, therefore there is - * a timeout for test execution. - */ - @Test - void testALotOfThreads() { - for (int i = 0; i < maxThreadsNumber; i++) { - testNThreads(i * i / 2); - } - } - - private void testNThreads(final int threadsNumber) { - - final Thread timer = new Thread(() -> { - Utils.sleep(5000); - synchronized (this) { - notify(); - } - }); - timer.start(); - - final ThreadPool pool = ThreadPoolFactory.initThreadPool(threadsNumber); - final List> list = new LinkedList<>(); - - for (int i = 0; i < threadsNumber - 1; i++) { - list.add(pool.addTask(new WaitSupplier())); - } - pool.addTask(new SleepSupplier()); - pool.addTask(new NotifySupplier()); - - new Thread(() -> { - pool.waitWithShutDown(); - synchronized (this) { - notify(); - } - }).start(); - - synchronized (this) { - try { - // there must be loop, but i cannot change variable - // for it in lambda... What should I do with it? - // Here I will hope that at least one test will fail. - wait(); - } catch (@NotNull final InterruptedException e) { - // do nothing - } - // Am i right that timer cannot still work? - // In case thread was set on this one immediately? - if (!timer.isAlive()) { - fail("Out of time."); - } - } - - for (final LightFuture lightFuture : list) { - assertTrue(lightFuture.isReady()); - } - } - - @Test - void testIsReady() { - final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); - final LightFuture future = pool.addTask(new SleepSupplier(250)); - pool.addTask(new SleepSupplier()); - - assertFalse(future.isReady()); - new Thread(() -> assertFalse(future.isReady())).start(); - Utils.sleep(500); - assertTrue(future.isReady()); - new Thread(() -> assertTrue(future.isReady())).start(); - } - - @Test - void testGet() { - final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); - - final ArrayList> list = new ArrayList<>(); - for (int i = 0; i < 10; i++) { - list.add(pool.addTask(new IntegerSupplier(i * i))); - } - - final Runnable r = () -> { - for (int i = 0; i < 10; i++) { - assertEquals(Integer.valueOf(i * i), list.get(i).get()); - } - }; - new Thread(r).start(); - new Thread(r).start(); - new Thread(r).start(); - r.run(); - } - - @Test - void testShutDown() { - final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); - final ArrayList> list = new ArrayList<>(); - - list.add(pool.addTask(new SleepSupplier())); - list.add(pool.addTask(new SleepSupplier())); - list.add(pool.addTask(new SleepSupplier())); - - pool.shutdown(); - - assertThrows(ThreadPoolIsTurnedDownException.class, - () -> pool.addTask(new IntegerSupplier(1))); - - pool.waitWithShutDown(); - - for (final LightFuture future : list) { - assertTrue(future.isReady()); - } - } - - @Test - void testExceptions() { - final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); - final LightFuture task = pool.addTask(() -> { - throw new RuntimeException(); - }); - - final Runnable check = () -> { - assertThrows(LightExecutionException.class, task::get); - assertThrows(LightExecutionException.class, - () -> task.thenApply(o -> 1).get()); - }; - - check.run(); - new Thread(check).start(); - new Thread(check).start(); - } - - @Test - void testThenApply() { - final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); - - final LightFuture task = pool.addTask(() -> 1); - for (int i = 0; i < 5; i++) { - pool.addTask(new SleepSupplier()); - } - - final Runnable r = () -> assertEquals(Integer.valueOf(2), task.thenApply(n -> n + 1).get()); - r.run(); - new Thread(r).start(); - new Thread(r).start(); - new Thread(r).start(); - - pool.waitWithShutDown(); - } -} \ No newline at end of file diff --git a/ThreadPool/src/test/java/suppliers/BaseSupplier.java b/ThreadPool/src/test/java/suppliers/BaseSupplier.java deleted file mode 100644 index 1d996a3..0000000 --- a/ThreadPool/src/test/java/suppliers/BaseSupplier.java +++ /dev/null @@ -1,14 +0,0 @@ -package suppliers; - -import java.util.function.Supplier; - -/** - * Base class for some simple suppliers, which may be used in tests. - * - * @param type of stored value. - */ -abstract class BaseSupplier implements Supplier { - - static final Object commonLock = new Object(); - -} diff --git a/ThreadPool/src/test/java/suppliers/CounterSupplier.java b/ThreadPool/src/test/java/suppliers/CounterSupplier.java deleted file mode 100644 index 4771169..0000000 --- a/ThreadPool/src/test/java/suppliers/CounterSupplier.java +++ /dev/null @@ -1,30 +0,0 @@ -package suppliers; - -import org.jetbrains.annotations.Nullable; - -public class CounterSupplier extends BaseSupplier { - - private static int counter; - private int myCounter; - - public int getCounter() { - return counter; - } - - @Nullable - @Override - public Void get() { - synchronized (this) { - myCounter++; - } - synchronized (commonLock) { - counter++; - } - return null; - } - - public int getMyCounter() { - return myCounter; - } - -} diff --git a/ThreadPool/src/test/java/suppliers/IntegerSupplier.java b/ThreadPool/src/test/java/suppliers/IntegerSupplier.java deleted file mode 100644 index 8162691..0000000 --- a/ThreadPool/src/test/java/suppliers/IntegerSupplier.java +++ /dev/null @@ -1,15 +0,0 @@ -package suppliers; - -public class IntegerSupplier extends BaseSupplier { - - private final Integer n; - - public IntegerSupplier(final int n) { - this.n = n; - } - - @Override - public Integer get() { - return n; - } -} diff --git a/ThreadPool/src/test/java/suppliers/NotifySupplier.java b/ThreadPool/src/test/java/suppliers/NotifySupplier.java deleted file mode 100644 index 52f0b8a..0000000 --- a/ThreadPool/src/test/java/suppliers/NotifySupplier.java +++ /dev/null @@ -1,15 +0,0 @@ -package suppliers; - -import org.jetbrains.annotations.Nullable; - -public class NotifySupplier extends BaseSupplier { - - @Nullable - @Override - public Void get() { - synchronized (commonLock) { - commonLock.notifyAll(); - } - return null; - } -} diff --git a/ThreadPool/src/test/java/suppliers/SleepSupplier.java b/ThreadPool/src/test/java/suppliers/SleepSupplier.java deleted file mode 100644 index 5173a2c..0000000 --- a/ThreadPool/src/test/java/suppliers/SleepSupplier.java +++ /dev/null @@ -1,24 +0,0 @@ -package suppliers; - -import org.jetbrains.annotations.Nullable; -import threadPool.Utils; - -public class SleepSupplier extends BaseSupplier { - - private int time = 100; - - public SleepSupplier() { - - } - - public SleepSupplier(final int time) { - this.time = time; - } - - @Nullable - @Override - public Void get() { - Utils.sleep(time); - return null; - } -} diff --git a/ThreadPool/src/test/java/suppliers/WaitSupplier.java b/ThreadPool/src/test/java/suppliers/WaitSupplier.java deleted file mode 100644 index 237e2d5..0000000 --- a/ThreadPool/src/test/java/suppliers/WaitSupplier.java +++ /dev/null @@ -1,21 +0,0 @@ -package suppliers; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class WaitSupplier extends BaseSupplier { - - @Nullable - @Override - public Void get() { - synchronized (commonLock) { - try { - commonLock.wait(); - } catch (@NotNull final InterruptedException e) { - //do nothing - } - } - return null; - } - -} From b39e539b63f1d9907793a8ed9f70b22687b54d09 Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Wed, 7 Mar 2018 16:29:08 +0300 Subject: [PATCH 18/19] tests are added to git --- .../kirakosian/threadPool/BlockingQueue.java | 54 ++++ .../kirakosian/threadPool/LightFuture.java | 39 +++ .../mit/kirakosian/threadPool/ThreadPool.java | 47 ++++ .../threadPool/ThreadPoolFactory.java | 15 ++ .../kirakosian/threadPool/ThreadPoolImpl.java | 238 ++++++++++++++++++ .../mit/kirakosian/threadPool/Utils.java | 15 ++ .../exceptions/LightExecutionException.java | 17 ++ .../TaskIsReadyAlreadyException.java | 13 + .../ThreadPoolIsTurnedDownException.java | 8 + .../mit/kirakosian/ThreadPoolFactoryTest.java | 199 +++++++++++++++ .../kirakosian/suppliers/BaseSupplier.java | 14 ++ .../kirakosian/suppliers/CounterSupplier.java | 30 +++ .../kirakosian/suppliers/IntegerSupplier.java | 15 ++ .../kirakosian/suppliers/NotifySupplier.java | 15 ++ .../kirakosian/suppliers/SleepSupplier.java | 24 ++ .../kirakosian/suppliers/WaitSupplier.java | 21 ++ 16 files changed, 764 insertions(+) create mode 100644 ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/BlockingQueue.java create mode 100644 ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/LightFuture.java create mode 100644 ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/ThreadPool.java create mode 100644 ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/ThreadPoolFactory.java create mode 100644 ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/ThreadPoolImpl.java create mode 100644 ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/Utils.java create mode 100644 ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/exceptions/LightExecutionException.java create mode 100644 ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/exceptions/TaskIsReadyAlreadyException.java create mode 100644 ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/exceptions/ThreadPoolIsTurnedDownException.java create mode 100644 ThreadPool/src/test/java/com/spbau/mit/kirakosian/ThreadPoolFactoryTest.java create mode 100644 ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/BaseSupplier.java create mode 100644 ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/CounterSupplier.java create mode 100644 ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/IntegerSupplier.java create mode 100644 ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/NotifySupplier.java create mode 100644 ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/SleepSupplier.java create mode 100644 ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/WaitSupplier.java diff --git a/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/BlockingQueue.java b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/BlockingQueue.java new file mode 100644 index 0000000..0544818 --- /dev/null +++ b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/BlockingQueue.java @@ -0,0 +1,54 @@ +package com.spbau.mit.kirakosian.threadPool; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * This class provides safe concurrent queue. + *

      + * In other words, this class is a wrapper around simple queue class, + * but in addition every action in concurrent program will be safe, that means, + * that you are not able to override any values in your queue, or to take + * the same value twice. + * + * @param type of the queue. + */ +@SuppressWarnings("WeakerAccess") +public class BlockingQueue { + + private final Queue tasks = new LinkedList<>(); + + /** + * Adds new item to the end of the queue. + * + * @param item item to add into queue. + */ + public void push(final T item) { + synchronized (tasks) { + tasks.add(item); + tasks.notify(); + } + } + + /** + * The method extracts top item from the queue. + * + * @return top item of the queue. + */ + @Nullable + public T pop() { + synchronized (tasks) { + while (tasks.isEmpty()) { + try { + tasks.wait(); + } catch (@NotNull final InterruptedException e) { + // do nothing + } + } + return tasks.poll(); + } + } +} \ No newline at end of file diff --git a/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/LightFuture.java b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/LightFuture.java new file mode 100644 index 0000000..2cc4c80 --- /dev/null +++ b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/LightFuture.java @@ -0,0 +1,39 @@ +package com.spbau.mit.kirakosian.threadPool; + +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * This interface provides methods for convenient tracking of your tasks. + *

      + * For example, you may check if execution of the task already has ended with + * {@link LightFuture#isReady()}, get result of the calculation at any time (task + * may be not finished yet) or add new tasks with dependency on the result of the + * calculation. + * + * @param + */ +public interface LightFuture extends Supplier { + + /** + * The method checks if any the task was already executed. Task may end + * with an error, this method still will return true. + * + * @return true if task was finished already and false otherwise. + */ + boolean isReady(); + + /** + * The method adds new task to the threadPool, which depends on the result of + * the calculation. That means, that task cannot be evaluated until its parent task + * is not ready. + *

      + * The method takes function f, and applies it to the result of the calculation. + * + * @param f function to apply + * @param type of the function result + * @return new LightFuture task, which was already added to the pool. + */ + LightFuture thenApply(Function f); + +} diff --git a/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/ThreadPool.java b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/ThreadPool.java new file mode 100644 index 0000000..b30ba29 --- /dev/null +++ b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/ThreadPool.java @@ -0,0 +1,47 @@ +package com.spbau.mit.kirakosian.threadPool; + +import java.util.function.Supplier; + +// In general, we may be able to work with any Task (Runnable interface), +// but here we want to work with LightFuture tasks only to provide user +// convenient interface to track tasks progress e.t.c. + +/** + * This interface provides base thread pool interface, which means that you are + * able to add new tasks to the pool and to call shutdown method, which turns the pool off. + *

      + * In addition, the pool provides convenient interface to track your tasks progress + * with the LightFuture interface. Every task will be wrapped in this interface. + */ +public interface ThreadPool { + + /** + * The method adds new task to the thread pool. Added tasks is equals to one "get" + * calculation of the supplier. + *

      + * In case of trying to add new task after shutdown ThreadPoolIsTurnedDownException + * will be thrown. + * + * @param supplier supplier to create task from. + * @param type of the result of calculation. + * @return new LightFuture object to track your task state. + */ + LightFuture addTask(Supplier supplier); + + /** + * Tells pool to shutdown. After calling this method no more tasks + * will be applied to the pool. All tasks, that are already stored + * will be finished. + *

      + * In case of trying to add new task after shutdown ThreadPoolIsTurnedDownException + * will be thrown. + */ + void shutdown(); + + /** + * This method stops your main thread until all tasks in poll will be executed + * and calls shutdown method of the pool, therefore after calling this method + * you might be sure that all tasks added to the pool are already finished. + */ + void waitWithShutDown(); +} diff --git a/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/ThreadPoolFactory.java b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/ThreadPoolFactory.java new file mode 100644 index 0000000..ea243b3 --- /dev/null +++ b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/ThreadPoolFactory.java @@ -0,0 +1,15 @@ +package com.spbau.mit.kirakosian.threadPool; + +/** + * Class with factory methods to create ThreadPool interface classes. + *

      + * By this class we hide out {@link ThreadPoolImpl} class and show only + * {@link ThreadPool} interface. + */ +@SuppressWarnings("WeakerAccess") +public class ThreadPoolFactory { + + public static ThreadPool initThreadPool(final int threadsNumber) { + return new ThreadPoolImpl(threadsNumber); + } +} diff --git a/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/ThreadPoolImpl.java b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/ThreadPoolImpl.java new file mode 100644 index 0000000..f1ca7fa --- /dev/null +++ b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/ThreadPoolImpl.java @@ -0,0 +1,238 @@ +package com.spbau.mit.kirakosian.threadPool; + +import com.spbau.mit.kirakosian.threadPool.exceptions.LightExecutionException; +import com.spbau.mit.kirakosian.threadPool.exceptions.TaskIsReadyAlreadyException; +import com.spbau.mit.kirakosian.threadPool.exceptions.ThreadPoolIsTurnedDownException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.LinkedList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * {@link ThreadPool} + *

      + * Basic ThreadPool interface implementation. + *

      + * This class supports safe work in concurrent environment. Class is not visible outside + * from the package, therefore it is possible to work with only with public methods + * of {@link ThreadPool} interface. + *

      + * In general, this pool supports only tasks, which implements LightFuture interface, + * but there is an easy way to work with any Runnable tasks, therefore blocking queue + * inside of the class stores any kind of Runnable classes. + */ +class ThreadPoolImpl implements ThreadPool { + + @Nullable + private final Supplier POISON_PILL = () -> null; + @Nullable + private final Task POISON_PILL_TASK = new Task<>(POISON_PILL); + + // Locks + private final Object shutDownLock = new Object(); + + private final BlockingQueue tasks = new BlockingQueue<>(); + + private final int threadsNumber; + private boolean isWorking = true; + private int threadsStillWorking; + + + ThreadPoolImpl(final int threadsNumber) { + this.threadsNumber = threadsNumber; + threadsStillWorking = threadsNumber; + final List threads = new LinkedList<>(); + for (int i = 0; i < threadsNumber; i++) { + threads.add(new Thread(new ThreadTask())); + } + for (final Thread thread : threads) { + thread.setDaemon(true); + thread.start(); + } + } + + /** + * Poison pill pattern is used for {@link ThreadPool#shutdown()} implementation. + */ + @Override + public synchronized void shutdown() { + if (isWorking) { // otherwise, we may do this section twice + for (int i = 0; i < threadsNumber; i++) { + addTask(POISON_PILL); + } + } + isWorking = false; + } + + /** + * {@link ThreadPool#waitWithShutDown()} + */ + @Override + public void waitWithShutDown() { + shutdown(); + synchronized (shutDownLock) { + while (threadsStillWorking != 0) { + try { + shutDownLock.wait(); + } catch (@NotNull final InterruptedException e) { + // do nothing + } + } + } + } + + /** + * {@link ThreadPool#addTask(Supplier)} + */ + @Nullable + @Override + public synchronized LightFuture addTask(final Supplier supplier) { + if (!isWorking) { + throw new ThreadPoolIsTurnedDownException(); + } + if (supplier == POISON_PILL) { + tasks.push(POISON_PILL_TASK); + return null; + } + final Task task = new Task<>(supplier); + tasks.push(task); + return task; + } + + /** + * Default task for every thread in the thread pool. + *

      + * Taking tasks from the queue until thread pool is working. + */ + private class ThreadTask implements Runnable { + + @Override + public void run() { + while (true) { + final Runnable task = tasks.pop(); + if (task == POISON_PILL_TASK) { + synchronized (shutDownLock) { + threadsStillWorking--; + if (threadsStillWorking == 0) { + shutDownLock.notifyAll(); + } + } + break; + } + if (task != null) { // there was a warning, so it is better to check for sure + task.run(); + } + } + } + } + + /** + * Simple LightFuture interface implementation, that may be used inside + * ThreadPoolImpl class. This class is not visible from outside, therefore you are + * not able to work with this class directly. + *

      + * Public interface of this class is safe in concurrent environment. + *

      + * Talking about not visible for user interface (such as method run, that is not + * visible with the interface LightFuture) it is expected, that programmer + * works safe with this methods (for example, calls run method twice). If there was + * any mistake in the code, TaskIsReadyAlreadyException is expected to be thrown. + *

      + * If an exception occurred during the task execution this exception will be stored + * inside of the class and passed to every future calculations (thenApply method). + * In case of trying to get result of the failed calculation or execute any + * corresponded on the answer task LightExecutionException will be thrown. + * + * @param return type of execution + */ + private class Task implements LightFuture, Runnable { + + private final Supplier supplier; + + private boolean ready; + private T value; + private Exception error; + + private Task(final Supplier supplier) { + this.supplier = supplier; + } + + /** + * Task is ready if it execution was ended by any thread. It does not matter + * either it was successful or not (ended with an exception). + *

      + * {@link LightFuture#isReady()} + * + * @return true if task was already completed and false otherwise. + */ + @Override + public boolean isReady() { + return ready; + } + + /** + * Returns the value of the calculation. If calculation is not ready yet, waits + * for its end and then returns the result. + *

      + * If there was an error during execution LightExecutionException with + * cause of the fail inside will be thrown. + *

      + * {@link LightFuture#get()} + * + * @return result of the calculation. + */ + @Override + public T get() { + synchronized (this) { + while (!ready) { // waiting for result + try { + wait(); + } catch (@NotNull final InterruptedException e) { + // nothing to do here + } + } + } + if (error != null) { + throw new LightExecutionException(error); + } + return value; + } + + + /** + * {@link LightFuture#thenApply(Function)} + */ + @Nullable + @Override + public LightFuture thenApply(@NotNull final Function f) { + final Supplier supplier = () -> f.apply(Task.this.get()); + return addTask(supplier); + } + + /** + * Inner method of class. Should not be visible from outside (of course it is + * possible to call this method with cast of this object to Runnable, + * but let us think that it is a user problem). + *

      + * This method is called, when thread from the pool staring to execute this task. + * Runnable interface is supported for this very reason. + */ + @Override + public synchronized void run() { + if (!ready) { // we may execute every task only one time + try { + value = supplier.get(); + } catch (@NotNull final Exception e) { + error = e; + } + ready = true; + notifyAll(); + } else { + throw new TaskIsReadyAlreadyException(); // should never happen, cause it is an inner method + } + } + } +} diff --git a/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/Utils.java b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/Utils.java new file mode 100644 index 0000000..866ecdc --- /dev/null +++ b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/Utils.java @@ -0,0 +1,15 @@ +package com.spbau.mit.kirakosian.threadPool; + +import org.jetbrains.annotations.NotNull; + +public class Utils { + + public static void sleep(final int time) { + try { + Thread.sleep(time); + } catch (@NotNull final InterruptedException e) { + e.printStackTrace(); + } + } + +} diff --git a/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/exceptions/LightExecutionException.java b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/exceptions/LightExecutionException.java new file mode 100644 index 0000000..c306037 --- /dev/null +++ b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/exceptions/LightExecutionException.java @@ -0,0 +1,17 @@ +package com.spbau.mit.kirakosian.threadPool.exceptions; + +/** + * This class provides exception to be thrown in case of calling get method + * of calculation, which was failed with th exception. + */ +public class LightExecutionException extends RuntimeException { + + @SuppressWarnings("unused") + public LightExecutionException() { + super(); + } + + public LightExecutionException(final Exception error) { + super(error); + } +} diff --git a/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/exceptions/TaskIsReadyAlreadyException.java b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/exceptions/TaskIsReadyAlreadyException.java new file mode 100644 index 0000000..4aa31ea --- /dev/null +++ b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/exceptions/TaskIsReadyAlreadyException.java @@ -0,0 +1,13 @@ +package com.spbau.mit.kirakosian.threadPool.exceptions; + +import com.spbau.mit.kirakosian.threadPool.LightFuture; + +/** + * This exception may be thrown if implementation of {@link LightFuture} + * interface methods was not concurrent safe. + *

      + * For example, if tasks method "run" was called more then one time. + */ +public class TaskIsReadyAlreadyException extends RuntimeException { + +} diff --git a/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/exceptions/ThreadPoolIsTurnedDownException.java b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/exceptions/ThreadPoolIsTurnedDownException.java new file mode 100644 index 0000000..c8ca245 --- /dev/null +++ b/ThreadPool/src/main/java/com/spbau/mit/kirakosian/threadPool/exceptions/ThreadPoolIsTurnedDownException.java @@ -0,0 +1,8 @@ +package com.spbau.mit.kirakosian.threadPool.exceptions; + +/** + * This exception is thrown in case of trying to add new task in the pool, that + * was already turned off. + */ +public class ThreadPoolIsTurnedDownException extends RuntimeException { +} diff --git a/ThreadPool/src/test/java/com/spbau/mit/kirakosian/ThreadPoolFactoryTest.java b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/ThreadPoolFactoryTest.java new file mode 100644 index 0000000..6e07da4 --- /dev/null +++ b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/ThreadPoolFactoryTest.java @@ -0,0 +1,199 @@ +package com.spbau.mit.kirakosian; + +import com.spbau.mit.kirakosian.suppliers.*; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; +import com.spbau.mit.kirakosian.threadPool.LightFuture; +import com.spbau.mit.kirakosian.threadPool.ThreadPool; +import com.spbau.mit.kirakosian.threadPool.ThreadPoolFactory; +import com.spbau.mit.kirakosian.threadPool.Utils; +import com.spbau.mit.kirakosian.threadPool.exceptions.LightExecutionException; +import com.spbau.mit.kirakosian.threadPool.exceptions.ThreadPoolIsTurnedDownException; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +@SuppressWarnings("FieldCanBeLocal") +class ThreadPoolFactoryTest { + + private final int testsNumber = 100; + private final int maxThreadsNumber = 10; + + /** + * This test checks if every task was executed exactly one time. + */ + @Test + void testCallEveryTaskOnce() { + for (int t = 0; t < testsNumber; t++) { + final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); + final List list = new LinkedList<>(); + + for (int i = 0; i < 10; i++) { + final CounterSupplier dummy = new CounterSupplier(); + list.add(dummy); + pool.addTask(dummy); + } + + pool.waitWithShutDown(); + + assertEquals((t + 1) * 10, list.get(0).getCounter()); + for (final CounterSupplier counterSupplier : list) { + assertEquals(1, counterSupplier.getMyCounter()); + } + } + } + + /** + * This test checks if there at least n threads int the pool. + *

      + * Initialises n - 1 thread with waiting tasks and notifying them with the last one. + * If there are not enough threads in the pool, test will stuck, therefore there is + * a timeout for test execution. + */ + @Test + void testALotOfThreads() { + for (int i = 0; i < maxThreadsNumber; i++) { + testNThreads(i * i / 2); + } + } + + private void testNThreads(final int threadsNumber) { + + final Thread timer = new Thread(() -> { + Utils.sleep(5000); + synchronized (this) { + notify(); + } + }); + timer.start(); + + final ThreadPool pool = ThreadPoolFactory.initThreadPool(threadsNumber); + final List> list = new LinkedList<>(); + + for (int i = 0; i < threadsNumber - 1; i++) { + list.add(pool.addTask(new WaitSupplier())); + } + pool.addTask(new SleepSupplier()); + pool.addTask(new NotifySupplier()); + + new Thread(() -> { + pool.waitWithShutDown(); + synchronized (this) { + notify(); + } + }).start(); + + synchronized (this) { + try { + // there must be loop, but i cannot change variable + // for it in lambda... What should I do with it? + // Here I will hope that at least one test will fail. + wait(); + } catch (@NotNull final InterruptedException e) { + // do nothing + } + // Am i right that timer cannot still work? + // In case thread was set on this one immediately? + if (!timer.isAlive()) { + fail("Out of time."); + } + } + + for (final LightFuture lightFuture : list) { + assertTrue(lightFuture.isReady()); + } + } + + @Test + void testIsReady() { + final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); + final LightFuture future = pool.addTask(new SleepSupplier(250)); + pool.addTask(new SleepSupplier()); + + assertFalse(future.isReady()); + new Thread(() -> assertFalse(future.isReady())).start(); + Utils.sleep(500); + assertTrue(future.isReady()); + new Thread(() -> assertTrue(future.isReady())).start(); + } + + @Test + void testGet() { + final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); + + final ArrayList> list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + list.add(pool.addTask(new IntegerSupplier(i * i))); + } + + final Runnable r = () -> { + for (int i = 0; i < 10; i++) { + assertEquals(Integer.valueOf(i * i), list.get(i).get()); + } + }; + new Thread(r).start(); + new Thread(r).start(); + new Thread(r).start(); + r.run(); + } + + @Test + void testShutDown() { + final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); + final ArrayList> list = new ArrayList<>(); + + list.add(pool.addTask(new SleepSupplier())); + list.add(pool.addTask(new SleepSupplier())); + list.add(pool.addTask(new SleepSupplier())); + + pool.shutdown(); + + assertThrows(ThreadPoolIsTurnedDownException.class, + () -> pool.addTask(new IntegerSupplier(1))); + + pool.waitWithShutDown(); + + for (final LightFuture future : list) { + assertTrue(future.isReady()); + } + } + + @Test + void testExceptions() { + final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); + final LightFuture task = pool.addTask(() -> { + throw new RuntimeException(); + }); + + final Runnable check = () -> { + assertThrows(LightExecutionException.class, task::get); + assertThrows(LightExecutionException.class, + () -> task.thenApply(o -> 1).get()); + }; + + check.run(); + new Thread(check).start(); + new Thread(check).start(); + } + + @Test + void testThenApply() { + final ThreadPool pool = ThreadPoolFactory.initThreadPool(3); + + final LightFuture task = pool.addTask(() -> 1); + for (int i = 0; i < 5; i++) { + pool.addTask(new SleepSupplier()); + } + + final Runnable r = () -> assertEquals(Integer.valueOf(2), task.thenApply(n -> n + 1).get()); + r.run(); + new Thread(r).start(); + new Thread(r).start(); + new Thread(r).start(); + + pool.waitWithShutDown(); + } +} \ No newline at end of file diff --git a/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/BaseSupplier.java b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/BaseSupplier.java new file mode 100644 index 0000000..f7dc459 --- /dev/null +++ b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/BaseSupplier.java @@ -0,0 +1,14 @@ +package com.spbau.mit.kirakosian.suppliers; + +import java.util.function.Supplier; + +/** + * Base class for some simple com.spbau.mit.kirakosian.suppliers, which may be used in tests. + * + * @param type of stored value. + */ +abstract class BaseSupplier implements Supplier { + + static final Object commonLock = new Object(); + +} diff --git a/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/CounterSupplier.java b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/CounterSupplier.java new file mode 100644 index 0000000..30185c3 --- /dev/null +++ b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/CounterSupplier.java @@ -0,0 +1,30 @@ +package com.spbau.mit.kirakosian.suppliers; + +import org.jetbrains.annotations.Nullable; + +public class CounterSupplier extends BaseSupplier { + + private static int counter; + private int myCounter; + + public int getCounter() { + return counter; + } + + @Nullable + @Override + public Void get() { + synchronized (this) { + myCounter++; + } + synchronized (commonLock) { + counter++; + } + return null; + } + + public int getMyCounter() { + return myCounter; + } + +} diff --git a/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/IntegerSupplier.java b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/IntegerSupplier.java new file mode 100644 index 0000000..bee9798 --- /dev/null +++ b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/IntegerSupplier.java @@ -0,0 +1,15 @@ +package com.spbau.mit.kirakosian.suppliers; + +public class IntegerSupplier extends BaseSupplier { + + private final Integer n; + + public IntegerSupplier(final int n) { + this.n = n; + } + + @Override + public Integer get() { + return n; + } +} diff --git a/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/NotifySupplier.java b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/NotifySupplier.java new file mode 100644 index 0000000..3c885ae --- /dev/null +++ b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/NotifySupplier.java @@ -0,0 +1,15 @@ +package com.spbau.mit.kirakosian.suppliers; + +import org.jetbrains.annotations.Nullable; + +public class NotifySupplier extends BaseSupplier { + + @Nullable + @Override + public Void get() { + synchronized (commonLock) { + commonLock.notifyAll(); + } + return null; + } +} diff --git a/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/SleepSupplier.java b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/SleepSupplier.java new file mode 100644 index 0000000..bcf2fc8 --- /dev/null +++ b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/SleepSupplier.java @@ -0,0 +1,24 @@ +package com.spbau.mit.kirakosian.suppliers; + +import org.jetbrains.annotations.Nullable; +import com.spbau.mit.kirakosian.threadPool.Utils; + +public class SleepSupplier extends BaseSupplier { + + private int time = 100; + + public SleepSupplier() { + + } + + public SleepSupplier(final int time) { + this.time = time; + } + + @Nullable + @Override + public Void get() { + Utils.sleep(time); + return null; + } +} diff --git a/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/WaitSupplier.java b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/WaitSupplier.java new file mode 100644 index 0000000..d1a84bd --- /dev/null +++ b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/suppliers/WaitSupplier.java @@ -0,0 +1,21 @@ +package com.spbau.mit.kirakosian.suppliers; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class WaitSupplier extends BaseSupplier { + + @Nullable + @Override + public Void get() { + synchronized (commonLock) { + try { + commonLock.wait(); + } catch (@NotNull final InterruptedException e) { + //do nothing + } + } + return null; + } + +} From b3e939d9c8243448623172beb9afb29e83d95978 Mon Sep 17 00:00:00 2001 From: Kirakosian Aleksander Date: Wed, 7 Mar 2018 16:31:33 +0300 Subject: [PATCH 19/19] more threads --- .../java/com/spbau/mit/kirakosian/ThreadPoolFactoryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ThreadPool/src/test/java/com/spbau/mit/kirakosian/ThreadPoolFactoryTest.java b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/ThreadPoolFactoryTest.java index 6e07da4..f5cb681 100644 --- a/ThreadPool/src/test/java/com/spbau/mit/kirakosian/ThreadPoolFactoryTest.java +++ b/ThreadPool/src/test/java/com/spbau/mit/kirakosian/ThreadPoolFactoryTest.java @@ -20,7 +20,7 @@ class ThreadPoolFactoryTest { private final int testsNumber = 100; - private final int maxThreadsNumber = 10; + private final int maxThreadsNumber = 20; /** * This test checks if every task was executed exactly one time.