Skip to content

Commit 854101b

Browse files
authored
Merge pull request iluwatar#529 from thomasoss/master
Thread Local Storage issue iluwatar#77
2 parents 286d6c3 + f84c4c1 commit 854101b

File tree

13 files changed

+928
-1
lines changed

13 files changed

+928
-1
lines changed

pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
</properties>
5151
<modules>
5252
<module>abstract-factory</module>
53+
<module>tls</module>
5354
<module>builder</module>
5455
<module>factory-method</module>
5556
<module>prototype</module>
@@ -469,4 +470,4 @@
469470
</plugins>
470471
</reporting>
471472

472-
</project>
473+
</project>

tls/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
layout: pattern
3+
title: Thread Local Storage
4+
folder: tls
5+
permalink: /patterns/tls/
6+
pumlid:
7+
categories: Concurrency
8+
tags:
9+
- Java
10+
- Difficulty-Intermediate
11+
---
12+
13+
## Intent
14+
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.
15+
16+
![alt text](./etc/tls.png "Thread Local Storage")
17+
18+
## Applicability
19+
Use the Thread Local Storage in any of the following situations
20+
21+
* 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
22+
* 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.

tls/etc/tls.png

33.7 KB
Loading

tls/etc/tls.ucls

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<class-diagram version="1.1.13" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
3+
associations="true" dependencies="false" nesting-relationships="true" router="FAN">
4+
<class id="1" language="java" name="com.iluwatar.tls.DateFormatCallable" project="PatternIluwatar"
5+
file="/PatternIluwatar/src/main/java/com/iluwatar/tls/DateFormatCallable.java" binary="false" corner="BOTTOM_RIGHT">
6+
<position height="-1" width="-1" x="503" y="227"/>
7+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
8+
sort-features="false" accessors="true" visibility="true">
9+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
10+
<operations public="true" package="true" protected="true" private="true" static="true"/>
11+
</display>
12+
</class>
13+
<interface id="2" language="java" name="java.util.concurrent.Callable" project="Intros"
14+
file="C:/interne/Programme/jdk8u60x64/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
15+
<position height="-1" width="-1" x="501" y="52"/>
16+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
17+
sort-features="false" accessors="true" visibility="true">
18+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
19+
<operations public="true" package="true" protected="true" private="true" static="true"/>
20+
</display>
21+
</interface>
22+
<class id="3" language="java" name="java.lang.ThreadLocal" project="Intros"
23+
file="C:/interne/Programme/jdk8u60x64/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
24+
<position height="-1" width="-1" x="111" y="284"/>
25+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
26+
sort-features="false" accessors="true" visibility="true">
27+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
28+
<operations public="true" package="true" protected="true" private="true" static="true"/>
29+
</display>
30+
</class>
31+
<class id="4" language="java" name="com.iluwatar.tls.App" project="PatternIluwatar"
32+
file="/PatternIluwatar/src/main/java/com/iluwatar/tls/App.java" binary="false" corner="BOTTOM_RIGHT">
33+
<position height="-1" width="-1" x="845" y="228"/>
34+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
35+
sort-features="false" accessors="true" visibility="true">
36+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
37+
<operations public="true" package="true" protected="true" private="true" static="true"/>
38+
</display>
39+
</class>
40+
<class id="5" language="java" name="com.iluwatar.tls.Result" project="PatternIluwatar"
41+
file="/PatternIluwatar/src/main/java/com/iluwatar/tls/Result.java" binary="false" corner="BOTTOM_RIGHT">
42+
<position height="-1" width="-1" x="673" y="459"/>
43+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
44+
sort-features="false" accessors="true" visibility="true">
45+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
46+
<operations public="true" package="true" protected="true" private="true" static="true"/>
47+
</display>
48+
</class>
49+
<dependency id="6">
50+
<end type="SOURCE" refId="1"/>
51+
<end type="TARGET" refId="5"/>
52+
</dependency>
53+
<association id="7">
54+
<end type="SOURCE" refId="1" navigable="false">
55+
<attribute id="8" name="df"/>
56+
<multiplicity id="9" minimum="0" maximum="1"/>
57+
</end>
58+
<end type="TARGET" refId="3" navigable="true"/>
59+
<display labels="true" multiplicity="true"/>
60+
</association>
61+
<realization id="10">
62+
<end type="SOURCE" refId="1"/>
63+
<end type="TARGET" refId="2"/>
64+
</realization>
65+
<dependency id="11">
66+
<end type="SOURCE" refId="4"/>
67+
<end type="TARGET" refId="5"/>
68+
</dependency>
69+
<dependency id="12">
70+
<end type="SOURCE" refId="4"/>
71+
<end type="TARGET" refId="1"/>
72+
</dependency>
73+
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
74+
sort-features="false" accessors="true" visibility="true">
75+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
76+
<operations public="true" package="true" protected="true" private="true" static="true"/>
77+
</classifier-display>
78+
<association-display labels="true" multiplicity="true"/>
79+
</class-diagram>

tls/etc/tls.urm.puml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
@startuml
2+
package com.iluwatar.tls {
3+
class App {
4+
+ App()
5+
+ main(args : String[]) {static}
6+
- printAndCountDates(res : Result) : int {static}
7+
- printAndCountExceptions(res : Result) : int {static}
8+
}
9+
class DateFormatCallable {
10+
- dateValue : String
11+
- df : ThreadLocal<DateFormat>
12+
+ DateFormatCallable(inDateFormat : String, inDateValue : String)
13+
+ call() : Result
14+
}
15+
class Result {
16+
- dateList : List<Date>
17+
- exceptionList : List<String>
18+
+ Result()
19+
+ getDateList() : List<Date>
20+
+ getExceptionList() : List<String>
21+
}
22+
}
23+
@enduml

tls/pom.xml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
4+
The MIT License
5+
Copyright (c) 2014-2016 Ilkka Seppälä
6+
2016 adapted for tls Thomas Bauer
7+
8+
Permission is hereby granted, free of charge, to any person obtaining a copy
9+
of this software and associated documentation files (the "Software"), to deal
10+
in the Software without restriction, including without limitation the rights
11+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
copies of the Software, and to permit persons to whom the Software is
13+
furnished to do so, subject to the following conditions:
14+
15+
The above copyright notice and this permission notice shall be included in
16+
all copies or substantial portions of the Software.
17+
18+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
THE SOFTWARE.
25+
26+
-->
27+
<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"
28+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
29+
<modelVersion>4.0.0</modelVersion>
30+
<parent>
31+
<groupId>com.iluwatar</groupId>
32+
<artifactId>java-design-patterns</artifactId>
33+
<version>1.15.0-SNAPSHOT</version>
34+
</parent>
35+
<artifactId>tls</artifactId>
36+
<dependencies>
37+
<dependency>
38+
<groupId>junit</groupId>
39+
<artifactId>junit</artifactId>
40+
<scope>test</scope>
41+
</dependency>
42+
</dependencies>
43+
</project>
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/**
2+
* The MIT License
3+
* Copyright (c) 2016 Thomas Bauer
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
24+
package com.iluwatar.tls;
25+
26+
import java.util.Calendar;
27+
import java.util.Date;
28+
import java.util.concurrent.ExecutorService;
29+
import java.util.concurrent.Executors;
30+
import java.util.concurrent.Future;
31+
32+
/**
33+
* ThreadLocal pattern
34+
* <p>
35+
* This App shows how to create an isolated space per each thread. In this
36+
* example the usage of SimpleDateFormat is made to be thread-safe. This is an
37+
* example of the ThreadLocal pattern.
38+
* <p>
39+
* By applying the ThreadLocal pattern you can keep track of application
40+
* instances or locale settings throughout the handling of a request. The
41+
* ThreadLocal class works like a static variable, with the exception that it is
42+
* only bound to the current thread! This allows us to use static variables in a
43+
* thread-safe way.
44+
* <p>
45+
* In Java, thread-local variables are implemented by the ThreadLocal class
46+
* object. ThreadLocal holds a variable of type T, which is accessible via get/set
47+
* methods.
48+
* <p>
49+
* SimpleDateFormat is one of the basic Java classes and is not thread-safe. If
50+
* you do not isolate the instance of SimpleDateFormat per each thread then
51+
* problems arise.
52+
* <p>
53+
* App converts the String date value 15/12/2015 to the Date format using the
54+
* Java class SimpleDateFormat. It does this 20 times using 4 threads, each doing
55+
* it 5 times. With the usage of as ThreadLocal in DateFormatCallable everything
56+
* runs well. But if you comment out the ThreadLocal variant (marked with "//TLTL")
57+
* and comment in the non ThreadLocal variant (marked with "//NTLNTL") you can
58+
* see what will happen without the ThreadLocal. Most likely you will get incorrect
59+
* date values and / or exceptions.
60+
* <p>
61+
* This example clearly show what will happen when using non thread-safe classes
62+
* in a thread. In real life this may happen one in of 1.000 or 10.000 conversions
63+
* and those are really hard to find errors.
64+
*
65+
* @author Thomas Bauer, 2017
66+
*/
67+
public class App {
68+
/**
69+
* Program entry point
70+
*
71+
* @param args
72+
* command line args
73+
*/
74+
public static void main(String[] args) {
75+
int counterDateValues = 0;
76+
int counterExceptions = 0;
77+
78+
// Create a callable
79+
DateFormatCallable callableDf = new DateFormatCallable("dd/MM/yyyy", "15/12/2015");
80+
// start 4 threads, each using the same Callable instance
81+
ExecutorService executor = Executors.newCachedThreadPool();
82+
83+
Future<Result> futureResult1 = executor.submit(callableDf);
84+
Future<Result> futureResult2 = executor.submit(callableDf);
85+
Future<Result> futureResult3 = executor.submit(callableDf);
86+
Future<Result> futureResult4 = executor.submit(callableDf);
87+
try {
88+
Result[] result = new Result[4];
89+
result[0] = futureResult1.get();
90+
result[1] = futureResult2.get();
91+
result[2] = futureResult3.get();
92+
result[3] = futureResult4.get();
93+
94+
// Print results of thread executions (converted dates and raised exceptions)
95+
// and count them
96+
for (int i = 0; i < result.length; i++) {
97+
counterDateValues = counterDateValues + printAndCountDates(result[i]);
98+
counterExceptions = counterExceptions + printAndCountExceptions(result[i]);
99+
}
100+
101+
// a correct run should deliver 20 times 15.12.2015
102+
// and a correct run shouldn't deliver any exception
103+
System.out.println("The List dateList contains " + counterDateValues + " date values");
104+
System.out.println("The List exceptionList contains " + counterExceptions + " exceptions");
105+
106+
} catch (Exception e) {
107+
System.out.println("Abnormal end of program. Program throws exception: " + e);
108+
}
109+
executor.shutdown();
110+
}
111+
112+
/**
113+
* Print result (date values) of a thread execution and count dates
114+
*
115+
* @param res contains results of a thread execution
116+
*/
117+
private static int printAndCountDates(Result res) {
118+
// a correct run should deliver 5 times 15.12.2015 per each thread
119+
int counter = 0;
120+
for (Date dt : res.getDateList()) {
121+
counter++;
122+
Calendar cal = Calendar.getInstance();
123+
cal.setTime(dt);
124+
// Formatted output of the date value: DD.MM.YYYY
125+
System.out.println(
126+
cal.get(Calendar.DAY_OF_MONTH) + "." + cal.get(Calendar.MONTH) + "." + +cal.get(Calendar.YEAR));
127+
}
128+
return counter;
129+
}
130+
131+
/**
132+
* Print result (exceptions) of a thread execution and count exceptions
133+
*
134+
* @param res contains results of a thread execution
135+
* @return number of dates
136+
*/
137+
private static int printAndCountExceptions(Result res) {
138+
// a correct run shouldn't deliver any exception
139+
int counter = 0;
140+
for (String ex : res.getExceptionList()) {
141+
counter++;
142+
System.out.println(ex);
143+
}
144+
return counter;
145+
}
146+
}

0 commit comments

Comments
 (0)