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..b16aa1fc6 --- /dev/null +++ b/tls/src/main/java/com/iluwatar/tls/App.java @@ -0,0 +1,108 @@ +/** + * 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.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +/** + * 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 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. These problems are described with the example {@link AppUgly} + * + */ +public class App { + // A list to collect the date values created in the the threads + static List dateList = Collections.synchronizedList(new ArrayList()); + + // A list to collect Exceptions thrown in the threads (should be none in + // this example) + static List exceptionList = Collections.synchronizedList(new ArrayList()); + + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + int counterDateValues = 0; + int counterExceptions = 0; + + // Create a runnable + DateFormatRunnable runnableDf = new DateFormatRunnable("dd/MM/yyyy", "15/12/2015"); + // start 4 threads, each using the same Runnable instance + Thread t1 = new Thread(runnableDf); + Thread t2 = new Thread(runnableDf); + Thread t3 = new Thread(runnableDf); + Thread t4 = new Thread(runnableDf); + t1.start(); + t2.start(); + t3.start(); + t4.start(); + try { + t1.join(); + t2.join(); + t3.join(); + t4.join(); + } catch (InterruptedException e) { + // Action not coded here + } + for (Date dt : dateList) { + // a correct run should deliver 20 times 15.12.2015 + counterDateValues++; + 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)); + } + for (String ex : exceptionList) { + // a correct run shouldn't deliver any exception + counterExceptions++; + System.out.println(ex); + } + System.out.println("The List dateList contains " + counterDateValues + " date values"); + System.out.println("The List exceptionList contains " + counterExceptions + " exceptions"); + } +} diff --git a/tls/src/main/java/com/iluwatar/tls/AppUgly.java b/tls/src/main/java/com/iluwatar/tls/AppUgly.java new file mode 100644 index 000000000..7025d42b7 --- /dev/null +++ b/tls/src/main/java/com/iluwatar/tls/AppUgly.java @@ -0,0 +1,112 @@ +/** + * 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.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +/** + * + * This App shows an example for problems with global thread variables. A global + * thread variable is a variable defined as class variable of a + * XyzRunnable-Class. In this example in the class + * {@link DateFormatUglyRunnable} + *

+ * Example use case: A well known problem with threads are non thread-safe Java + * classes. One example is the class SimpleDateFormat. + *

+ * In the example an instance of SimpleDateFormat is created in the constructor + * of the Runnable. The constructor initializes a class variable with the + * instance. The Runnable only does convert a string date to a date format using + * the instance of SimpleDateFormat. It does this 5 times. + *

+ * In the example 4 threads are started to do the same. If you are lucky + * everything works well. But if you try more often you will happen to see + * Exceptions. And these Exceptions arise on arbitrary points of the run. + *

+ * The reason for this exceptions is: The threads try to use internal instance + * variables of the SimpleDateFormat instance at the same time (the date is + * stored internal as member variable during parsing and may be overwritten by + * another thread during a parse is still running) + *

+ * And even without Exceptions the run may not deliver the correct results. + * All date values should be the same after the run. Check it + * + */ +public class AppUgly { + // A list to collect the date values created in the the threads + static List dateList = Collections.synchronizedList(new ArrayList()); + // A list to collect Exceptions thrown in the threads + static List exceptionList = Collections.synchronizedList(new ArrayList()); + + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + int counterDateValues = 0; + int counterExceptions = 0; + + // Prepare the Runnable + DateFormatUglyRunnable runnableDf = new DateFormatUglyRunnable("dd/MM/yyyy", "15/12/2015"); + + // 4 threads using the same Runnable + Thread t1 = new Thread(runnableDf); + Thread t2 = new Thread(runnableDf); + Thread t3 = new Thread(runnableDf); + Thread t4 = new Thread(runnableDf); + t1.start(); + t2.start(); + t3.start(); + t4.start(); + try { + t1.join(); + t2.join(); + t3.join(); + t4.join(); + } catch (InterruptedException e) { + // Action not coded here + } + for (Date dt : dateList) { + // a correct run should deliver 20 times 15.12.2015 + counterDateValues++; + 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)); + } + for (String ex : exceptionList) { + // a correct run shouldn't deliver any exception + counterExceptions++; + System.out.println(ex); + } + System.out.println("The List dateList contains " + counterDateValues + " date values"); + System.out.println("The List exceptionList contains " + counterExceptions + " exceptions"); + } +} diff --git a/tls/src/main/java/com/iluwatar/tls/DateFormatRunnable.java b/tls/src/main/java/com/iluwatar/tls/DateFormatRunnable.java new file mode 100644 index 000000000..d2009d21b --- /dev/null +++ b/tls/src/main/java/com/iluwatar/tls/DateFormatRunnable.java @@ -0,0 +1,84 @@ +/** + * 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; + +/** + * DateFormatRunnable converts string dates to a date format using + * SimpleDateFormat The date format and the date value will be passed to the + * Runnable 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} + * + */ +public class DateFormatRunnable implements Runnable { + // class variables (members) + private ThreadLocal df; + private String dateValue; // for date Value 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 DateFormatRunnable(String inDateFormat, String inDateValue) { + final String idf = inDateFormat; + this.df = new ThreadLocal() { + @Override + protected DateFormat initialValue() { + return new SimpleDateFormat(idf); + } + }; + this.dateValue = inDateValue; + } + + /** + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + System.out.println(Thread.currentThread() + " started executing..."); + + // 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 + App.dateList.add(this.df.get().parse(this.dateValue)); + } catch (Exception e) { + // write the Exception to a list and continue work + App.exceptionList.add(e.getClass() + ": " + e.getMessage()); + } + + } + + System.out.println(Thread.currentThread() + " finished executing"); + } +} diff --git a/tls/src/main/java/com/iluwatar/tls/DateFormatUglyRunnable.java b/tls/src/main/java/com/iluwatar/tls/DateFormatUglyRunnable.java new file mode 100644 index 000000000..ca883a913 --- /dev/null +++ b/tls/src/main/java/com/iluwatar/tls/DateFormatUglyRunnable.java @@ -0,0 +1,76 @@ +/** + * 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; + +/** + * DateFormatUglyRunnable converts string dates to a date format using + * SimpleDateFormat. The date value and the date format will be passed to the + * Runnable by the constructor. The constructor creates an instance of + * SimpleDateFormat and stores it in a class variable. For the complete + * description of the example see {@link AppUgly} + * + */ +public class DateFormatUglyRunnable implements Runnable { + // class variables (members) + private DateFormat df; + private String dateValue; + + /** + * 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 DateFormatUglyRunnable(String inDateFormat, String inDateValue) { + this.df = new SimpleDateFormat(inDateFormat); + this.dateValue = inDateValue; + } + + /** + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + System.out.println(Thread.currentThread() + " started executing..."); + + // Convert date value to date 5 times + for (int i = 1; i <= 5; i++) { + try { + // Create the date value and store it in dateList + AppUgly.dateList.add(this.df.parse(this.dateValue)); + } catch (Exception e) { + // write the Exception to a list and continue work + AppUgly.exceptionList.add(e.getClass() + ": " + e.getMessage()); + } + + } + + System.out.println(Thread.currentThread() + " finished executing"); + } +}