Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
aeefc11
Merge pull request #1 from iluwatar/master
thomasoss Dec 14, 2016
d80edd1
Create test
thomasoss Dec 15, 2016
7067d1a
Create test
thomasoss Dec 15, 2016
c4eb198
Upload code files
thomasoss Dec 15, 2016
544f7fb
delete empty file
thomasoss Dec 15, 2016
15d660b
empty file to create folder
thomasoss Dec 15, 2016
e64f7fa
upload junit test
thomasoss Dec 15, 2016
803a972
delete empty file
thomasoss Dec 15, 2016
937a1e2
delete empty file
thomasoss Dec 15, 2016
e17cf27
upload pom.xml
thomasoss Dec 15, 2016
9b7ce55
Create emptyfile
thomasoss Dec 16, 2016
487f9dd
Add files via upload
thomasoss Dec 16, 2016
61b1356
Delete emptyfile
thomasoss Dec 16, 2016
c8b3c77
Add files via upload
thomasoss Dec 16, 2016
fb98da8
Add files via upload
thomasoss Dec 16, 2016
e210e4e
Add files via upload
thomasoss Dec 16, 2016
26b79a5
Update README.md
thomasoss Jan 29, 2017
e8fc342
Update README.md
thomasoss Jan 29, 2017
c167f87
Delete tls.png
thomasoss Jan 29, 2017
3342851
Delete tls.ucls
thomasoss Jan 29, 2017
c529e35
Delete tls.urm.puml
thomasoss Jan 29, 2017
65e0479
Create empty
thomasoss Jan 29, 2017
15913d6
Add files via upload
thomasoss Jan 29, 2017
283f198
Delete empty
thomasoss Jan 29, 2017
ed11c4c
Delete App.java
thomasoss Jan 29, 2017
c598748
Delete AppUgly.java
thomasoss Jan 29, 2017
f170aaa
Delete DateFormatRunnable.java
thomasoss Jan 29, 2017
7200329
Add files via upload
thomasoss Jan 29, 2017
080965f
Delete DateFormatUglyRunnable.java
thomasoss Jan 29, 2017
a8e2c15
Add files via upload
thomasoss Jan 29, 2017
453862c
Delete AppTest.java
thomasoss Jan 29, 2017
6202f3a
Add files via upload
thomasoss Jan 29, 2017
3324e1b
Update pom.xml
thomasoss Jan 29, 2017
3d3dd58
Update pom.xml
thomasoss Jan 29, 2017
59ea207
Delete DateFormatRunnableTest.java
thomasoss Jan 29, 2017
82f8460
Delete DateFormatRunnableTestIncorrectDateFormat.java
thomasoss Jan 29, 2017
2bbf842
Delete DateFormatRunnableTestMultiThread.java
thomasoss Jan 29, 2017
ddac9dc
Add files via upload
thomasoss Jan 29, 2017
fd71076
Update pom.xml
thomasoss Feb 18, 2017
2c2d874
Update App.java
thomasoss Mar 18, 2017
f84c4c1
Update App.java
thomasoss Mar 18, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.14.0-SNAPSHOT</version>
<version>1.15.0-SNAPSHOT</version>
<packaging>pom</packaging>
<inceptionYear>2014</inceptionYear>
<properties>
Expand Down Expand Up @@ -50,6 +50,7 @@
</properties>
<modules>
<module>abstract-factory</module>
<module>tls</module>
<module>builder</module>
<module>factory-method</module>
<module>prototype</module>
Expand Down Expand Up @@ -466,4 +467,4 @@
</plugins>
</reporting>

</project>
</project>
22 changes: 22 additions & 0 deletions tls/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
layout: pattern
title: Thread Local Storage
folder: tls
permalink: /patterns/tls/
pumlid:
categories: Concurrency
tags:
- Java
- Difficulty-Intermediate
---

## Intent
Securing variables global to a thread against being spoiled by other threads. That is needed if you use class variables or static variables in your Callable object or Runnable object that are not read-only.

![alt text](./etc/tls.png "Thread Local Storage")

## Applicability
Use the Thread Local Storage in any of the following situations

* when you use class variables in your Callable / Runnalbe object that are not read-only and you use the same Callable instance in more than one thread running in parallel
* when you use static variables in your Callable / Runnable object that are not read-only and more than one instances of the Callable / Runnalbe may run in parallel threads.
Binary file added tls/etc/tls.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 79 additions & 0 deletions tls/etc/tls.ucls
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.13" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
associations="true" dependencies="false" nesting-relationships="true" router="FAN">
<class id="1" language="java" name="com.iluwatar.tls.DateFormatCallable" project="PatternIluwatar"
file="/PatternIluwatar/src/main/java/com/iluwatar/tls/DateFormatCallable.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="503" y="227"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<interface id="2" language="java" name="java.util.concurrent.Callable" project="Intros"
file="C:/interne/Programme/jdk8u60x64/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="501" y="52"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<class id="3" language="java" name="java.lang.ThreadLocal" project="Intros"
file="C:/interne/Programme/jdk8u60x64/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="111" y="284"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="4" language="java" name="com.iluwatar.tls.App" project="PatternIluwatar"
file="/PatternIluwatar/src/main/java/com/iluwatar/tls/App.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="845" y="228"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="5" language="java" name="com.iluwatar.tls.Result" project="PatternIluwatar"
file="/PatternIluwatar/src/main/java/com/iluwatar/tls/Result.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="673" y="459"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<dependency id="6">
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="5"/>
</dependency>
<association id="7">
<end type="SOURCE" refId="1" navigable="false">
<attribute id="8" name="df"/>
<multiplicity id="9" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="3" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<realization id="10">
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="2"/>
</realization>
<dependency id="11">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="5"/>
</dependency>
<dependency id="12">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="1"/>
</dependency>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>
23 changes: 23 additions & 0 deletions tls/etc/tls.urm.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@startuml
package com.iluwatar.tls {
class App {
+ App()
+ main(args : String[]) {static}
- printAndCountDates(res : Result) : int {static}
- printAndCountExceptions(res : Result) : int {static}
}
class DateFormatCallable {
- dateValue : String
- df : ThreadLocal<DateFormat>
+ DateFormatCallable(inDateFormat : String, inDateValue : String)
+ call() : Result
}
class Result {
- dateList : List<Date>
- exceptionList : List<String>
+ Result()
+ getDateList() : List<Date>
+ getExceptionList() : List<String>
}
}
@enduml
43 changes: 43 additions & 0 deletions tls/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?xml version="1.0"?>
<!--

The MIT License
Copyright (c) 2014-2016 Ilkka Seppälä
2016 adapted for tls 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.

-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.15.0-SNAPSHOT</version>
</parent>
<artifactId>tls</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
146 changes: 146 additions & 0 deletions tls/src/main/java/com/iluwatar/tls/App.java
Original file line number Diff line number Diff line change
@@ -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
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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<Result> futureResult1 = executor.submit(callableDf);
Future<Result> futureResult2 = executor.submit(callableDf);
Future<Result> futureResult3 = executor.submit(callableDf);
Future<Result> 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) {
System.out.println("Abnormal end of program. Program throws exception: " + e);
}
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;
}
}
Loading