diff --git a/tls/src/main/java/com/iluwatar/tls/App.java b/tls/src/main/java/com/iluwatar/tls/App.java new file mode 100644 index 000000000..80e87042c --- /dev/null +++ b/tls/src/main/java/com/iluwatar/tls/App.java @@ -0,0 +1,146 @@ +/** + * The MIT License + * Copyright (c) 2016 Thomas Bauer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.tls; + +import java.util.Calendar; +import java.util.Date; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/** + * ThreadLocal pattern + *

+ * This App shows how to create an isolated space per each thread. In this + * example the usage of SimpleDateFormat is made to be thread-safe. This is an + * example of the ThreadLocal pattern. + *

+ * By applying the ThreadLocal pattern you can keep track of application + * instances or locale settings throughout the handling of a request. The + * ThreadLocal class works like a static variable, with the exception that it is + * only bound to the current thread! This allows us to use static variables in a + * thread-safe way. + *

+ * In Java, thread-local variables are implemented by the ThreadLocal class + * object. ThreadLocal holds a variable of type T, which is accessible via get/set + * methods. + *

+ * SimpleDateFormat is one of the basic Java classes and is not thread-safe. If + * you do not isolate the instance of SimpleDateFormat per each thread then + * problems arise. + *

+ * App converts the String date value 15/12/2015 to the Date format using the + * Java class SimpleDateFormat. It does this 20 times using 4 threads, each doing + * it 5 times. With the usage of as ThreadLocal in DateFormatCallable everything + * runs well. But if you comment out the ThreadLocal variant (marked with "//TLTL") + * and comment in the non ThreadLocal variant (marked with "//NTLNTL") you can + * see what will happen without the ThreadLocal. Most likely you will get incorrect + * date values and / or exceptions. + *

+ * This example clearly show what will happen when using non thread-safe classes + * in a thread. In real life this may happen one in of 1.000 or 10.000 conversions + * and those are really hard to find errors. + * + * @author Thomas Bauer, 2017 + */ +public class App { + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + int counterDateValues = 0; + int counterExceptions = 0; + + // Create a callable + DateFormatCallable callableDf = new DateFormatCallable("dd/MM/yyyy", "15/12/2015"); + // start 4 threads, each using the same Callable instance + ExecutorService executor = Executors.newCachedThreadPool(); + + Future futureResult1 = executor.submit(callableDf); + Future futureResult2 = executor.submit(callableDf); + Future futureResult3 = executor.submit(callableDf); + Future futureResult4 = executor.submit(callableDf); + try { + Result[] result = new Result[4]; + result[0] = futureResult1.get(); + result[1] = futureResult2.get(); + result[2] = futureResult3.get(); + result[3] = futureResult4.get(); + + // Print results of thread executions (converted dates and raised exceptions) + // and count them + for (int i = 0; i < result.length; i++) { + counterDateValues = counterDateValues + printAndCountDates(result[i]); + counterExceptions = counterExceptions + printAndCountExceptions(result[i]); + } + + // a correct run should deliver 20 times 15.12.2015 + // and a correct run shouldn't deliver any exception + System.out.println("The List dateList contains " + counterDateValues + " date values"); + System.out.println("The List exceptionList contains " + counterExceptions + " exceptions"); + + } catch (Exception e) { + // no action here + } + executor.shutdown(); + } + + /** + * Print result (date values) of a thread execution and count dates + * + * @param res contains results of a thread execution + */ + private static int printAndCountDates(Result res) { + // a correct run should deliver 5 times 15.12.2015 per each thread + int counter = 0; + for (Date dt : res.getDateList()) { + counter++; + Calendar cal = Calendar.getInstance(); + cal.setTime(dt); + // Formatted output of the date value: DD.MM.YYYY + System.out.println( + cal.get(Calendar.DAY_OF_MONTH) + "." + cal.get(Calendar.MONTH) + "." + +cal.get(Calendar.YEAR)); + } + return counter; + } + + /** + * Print result (exceptions) of a thread execution and count exceptions + * + * @param res contains results of a thread execution + * @return number of dates + */ + private static int printAndCountExceptions(Result res) { + // a correct run shouldn't deliver any exception + int counter = 0; + for (String ex : res.getExceptionList()) { + counter++; + System.out.println(ex); + } + return counter; + } +} diff --git a/tls/src/main/java/com/iluwatar/tls/DateFormatCallable.java b/tls/src/main/java/com/iluwatar/tls/DateFormatCallable.java new file mode 100644 index 000000000..a28e24d4e --- /dev/null +++ b/tls/src/main/java/com/iluwatar/tls/DateFormatCallable.java @@ -0,0 +1,98 @@ +/** + * The MIT License + * Copyright (c) 2016 Thomas Bauer + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.tls; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.concurrent.Callable; + +/** + * DateFormatCallable converts string dates to a date format using + * SimpleDateFormat. The date format and the date value will be passed to the + * Callable by the constructor. The constructor creates a instance of + * SimpleDateFormat and stores it in a ThreadLocal class variable. For the + * complete description of the example see {@link App} + * + * You can comment out the code marked with //TLTL and comment in the + * code marked //NTLNTL. Then you can see what will happen if you do not + * use the ThreadLocal. For details see the description of {@link App} + * + * @author Thomas Bauer, 2017 + */ +public class DateFormatCallable implements Callable { + // class variables (members) + private ThreadLocal df; //TLTL + // private DateFormat df; //NTLNTL + + private String dateValue; // for dateValue Thread Local not needed + + + /** + * The date format and the date value are passed to the constructor + * + * @param inDateFormat + * string date format string, e.g. "dd/MM/yyyy" + * @param inDateValue + * string date value, e.g. "21/06/2016" + */ + public DateFormatCallable(String inDateFormat, String inDateValue) { + final String idf = inDateFormat; //TLTL + this.df = new ThreadLocal() { //TLTL + @Override //TLTL + protected DateFormat initialValue() { //TLTL + return new SimpleDateFormat(idf); //TLTL + } //TLTL + }; //TLTL + // this.df = new SimpleDateFormat(inDateFormat); //NTLNTL + this.dateValue = inDateValue; + } + + /** + * @see java.util.concurrent.Callable#call() + */ + @Override + public Result call() { + System.out.println(Thread.currentThread() + " started executing..."); + Result result = new Result(); + + // Convert date value to date 5 times + for (int i = 1; i <= 5; i++) { + try { + // this is the statement where it is important to have the + // instance of SimpleDateFormat locally + // Create the date value and store it in dateList + result.getDateList().add(this.df.get().parse(this.dateValue)); //TLTL +// result.getDateList().add(this.df.parse(this.dateValue)); //NTLNTL + } catch (Exception e) { + // write the Exception to a list and continue work + result.getExceptionList().add(e.getClass() + ": " + e.getMessage()); + } + + } + + System.out.println(Thread.currentThread() + " finished processing part of the thread"); + + return result; + } +} diff --git a/tls/src/main/java/com/iluwatar/tls/Result.java b/tls/src/main/java/com/iluwatar/tls/Result.java new file mode 100644 index 000000000..951a02a2f --- /dev/null +++ b/tls/src/main/java/com/iluwatar/tls/Result.java @@ -0,0 +1,40 @@ +/* + * Fiducia IT AG, All rights reserved. Use is subject to license terms. + */ + +package com.iluwatar.tls; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Result object that will be returned by the Callable {@link DateFormatCallable} + * used in {@link App} + * + * @author Thomas Bauer, 2017 + */ +public class Result { + // A list to collect the date values created in one thread + private List dateList = new ArrayList(); + + // A list to collect Exceptions thrown in one threads (should be none in + // this example) + private List exceptionList = new ArrayList(); + + /** + * + * @return List of date values collected within an thread execution + */ + public List getDateList() { + return dateList; + } + + /** + * + * @return List of exceptions thrown within an thread execution + */ + public List getExceptionList() { + return exceptionList; + } +}