Coming across blog entry "
Who said Java Hashtable is thread safe?", I decided to run the code. He's right in saying that Hashtable isn't thread safe, though, I don't think such a claim was ever officially made. Maybe if there is allusion among some that it is, then I suppose it needs dispelling. Thread safety is a design challenge. Fully synchronized classes, and even completely thread-safe classes can be used inefficiently, or worse, incorrectly, in a concurrent system.
Here are three examples of thread unsafe usage, followed by three examples of safe usage:
1. Unsafe
package unsafe.demo;
import java.util.HashMap;
import java.util.Map;
import unsafe.threads.Unsafe_Hashmap_NoSynchronized_NotUsingContainsKey;
public class DemoThreadUnsafe {
private Map<Integer, String> map = new HashMap<Integer, String>();
public static void main(String[] args) throws InterruptedException {
for (int j = 1; j < 200; j++) {
DemoThreadUnsafe demoClass = new DemoThreadUnsafe();
for (int i = 0; i < 5; i++) {
new Thread(new Unsafe_Hashmap_NoSynchronized_NotUsingContainsKey(demoClass.map)).start();
}
Thread.currentThread().sleep(10);
System.out.println(demoClass.map.keySet());
demoClass.map.clear();
}
}
}
|
1. Unsafe cont.
package unsafe.threads;
import java.util.Map;
/**
* put operation is not safe on the map because its impl in
* hashmap (used by calling thread) is not synchronized
* based on: http://lovehasija.com/2012/08/16/who-said-java-hashtable-is-thread-safe/
*
* @author sshakil
*
*/
public class Unsafe_Hashmap_NoSynchronized_NotUsingContainsKey implements Runnable {
private Map<Integer, String> map = null;
public Unsafe_Hashmap_NoSynchronized_NotUsingContainsKey(Map<Integer, String> map) {
this.map = map;
}
@Override
public void run() {
for (int i = 1; i <= 30; i++) {
try {
map.put(i, Thread.currentThread().getName());
} catch (Exception e) {
System.out.println(e);
}
}
}
}
|
2. Unsafe
package unsafe.demo;
import java.util.Hashtable;
import java.util.Map;
import unsafe.threads.Unsafe_Hashtable_NoSynchronized_UsingContainsKey;
public class DemoThreadUnsafe2 {
private static volatile Map<Integer, String> map = new Hashtable<Integer, String>();
public static void main(String[] args) throws InterruptedException {
for (int j = 1; j < 200; j++) {
DemoThreadUnsafe2 demoClass = new DemoThreadUnsafe2();
for (int i = 0; i < 10; i++) {
new Thread(new Unsafe_Hashtable_NoSynchronized_UsingContainsKey(demoClass.map)).start();
}
Thread.currentThread().sleep(100);
demoClass.map.clear();
}
}
}
|
2. Unsafe cont.
package unsafe.threads;
import java.util.Map;
/**
* the sequence of map.containsKey(i) followed by the map.put sequence is not
* safe because map's impl in hashmap (used by calling thread) is not
* synchronized based on:
* http://lovehasija.com/2012/08/16/who-said-java-hashtable-is-thread-safe/
*
* @author sshakil
*
*/
public class Unsafe_Hashtable_NoSynchronized_UsingContainsKey implements Runnable {
private static volatile Map<Integer, String> map = null;
public Unsafe_Hashtable_NoSynchronized_UsingContainsKey(Map<Integer, String> map) {
this.map = map;
}
@Override
public void run() {
for (int i = 1; i <= 30; i++) {
synchronized(map) {
if(!map.containsKey(i)) {
//some other thread can modify data between the execution of
//above and below lines
//try to catch it:
if(map.containsKey(i)) {
try {
throw new Exception ("Detected mofification by another thread.");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//more modifications can occur any time in above block
System.out.println( Thread.currentThread().getName()
+ "\tInserting new, Key exists: " + map.containsKey(i));
map.put(i, Thread.currentThread().getName());
}
}
}
}
}
|
3. Unsafe
package unsafe.demo;
import java.util.concurrent.ConcurrentHashMap;
import safe.threads.Safe_Hashtable_Synchronized_UsingContainsKey;
import unsafe.threads.Unsafe_ConcurrentHashMap_NoSynchronized_UsingContainsKey;
public class DemoThreadUnsafe3 {
// private Map<Integer, String> map = new Hashtable<Integer, String>();
private ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<Integer, String>();
public static void main(String[] args) throws InterruptedException {
for (int j = 1; j < 2000; j++) {
DemoThreadUnsafe3 demoClass = new DemoThreadUnsafe3();
for (int i = 0; i < 15; i++) {
new Thread(
new Unsafe_ConcurrentHashMap_NoSynchronized_UsingContainsKey(demoClass.map))
.start();
}
Thread.currentThread().sleep(1);
demoClass.map.clear();
}
}
} |
3. Unsafe cont.
package unsafe.threads;
import java.util.Map;
public class Unsafe_ConcurrentHashMap_NoSynchronized_UsingContainsKey implements Runnable {
private Map<Integer, String> map = null;
public Unsafe_ConcurrentHashMap_NoSynchronized_UsingContainsKey(Map<Integer, String> map) {
this.map = map;
}
@Override
public void run() {
for (int i = 1; i <= 30; i++) {
//synchronized(map) {
if(!map.containsKey(i)) {
if(map.containsKey(i)){
System.out.println("TRUE?");
try {
throw new Exception("this can't be!");
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println( Thread.currentThread().getName()
+ "\tInserting new, Key exists: " + map.containsKey(i));
map.put(i, Thread.currentThread().getName());
}
//}
}
}
}
|
Examples of safe usage:
1. Safe
package safe.demo;
import java.util.Hashtable;
import safe.threads.Safe_Hashtable_NoSynchronize_UsingPut;
/**
* Thread safe hashmap usage through the use of 'synchronized' keyword. Based
* on: http://lovehasija.com/2012/08/16/who-said-java-hashtable-is-thread-safe/
*
* @author sshakil
*
*/
public class PartiallySafe_BecauseOfHashtable {
// private Map<Integer, String> map = new Hashtable<Integer, String>();
private Hashtable<Integer, String> map = new Hashtable<Integer, String>();
public static void main(String[] args) throws InterruptedException {
for (int j = 1; j < 100; j++) {
PartiallySafe_BecauseOfHashtable demoClass = new PartiallySafe_BecauseOfHashtable();
for (int i = 0; i < 5; i++) {
new Thread(new Safe_Hashtable_NoSynchronize_UsingPut(demoClass.map)).start();
}
Thread.currentThread().sleep(10);
System.out.println(demoClass.map.keySet());
demoClass.map.clear();
}
}
}
|
1. Safe cont.
package safe.threads;
import java.util.Map;
/**
* put operation is not safe on the map because its impl in
* hashmap (used by calling thread) is not synchronized
* based on: http://lovehasija.com/2012/08/16/who-said-java-hashtable-is-thread-safe/
*
* @author sshakil
*
*/
public class Safe_Hashtable_NoSynchronize_UsingPut implements Runnable {
private Map<Integer, String> map = null;
public Safe_Hashtable_NoSynchronize_UsingPut(Map<Integer, String> map) {
this.map = map;
}
@Override
public void run() {
for (int i = 1; i <= 30; i++) {
try {
map.put(i, Thread.currentThread().getName());
} catch (Exception e) {
System.out.println(e);
}
}
}
}
|
2. Safe
package safe.demo;
import java.util.concurrent.ConcurrentHashMap;
import safe.threads.Safe_ConcurrentHashMap_NoSynchronize_UsingPutIfAbsent;
public class Safe_ConcurrentHashMap {
private ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<Integer, String>(
80, 0.5f, 200);
// private Map<Integer, String> map = new HashMap<Integer, String>();
public static void main(String[] args) throws InterruptedException {
for (int j = 1; j < 100; j++) {
Safe_ConcurrentHashMap demoClass = new Safe_ConcurrentHashMap();
for (int i = 0; i < 5; i++) {
new Thread(
new Safe_ConcurrentHashMap_NoSynchronize_UsingPutIfAbsent(demoClass.map))
.start();
}
Thread.currentThread().sleep(10);
System.out.println(demoClass.map.keySet());
demoClass.map.clear();
}
}
}
|
2. Safe cont.
package safe.threads;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Thread safe hashmap usage through the use of 'synchronized' keyword based on:
* http://lovehasija.com/2012/08/16/who-said-java-hashtable-is-thread-safe/
*
* @author sshakil
*/
public class Safe_ConcurrentHashMap_NoSynchronize_UsingPutIfAbsent implements Runnable {
//private Map<Integer, String> map = null;
private ConcurrentHashMap<Integer, String> map = null;
public Safe_ConcurrentHashMap_NoSynchronize_UsingPutIfAbsent(
ConcurrentHashMap<Integer, String> map) {
//public ThreadSafeMapUsage3(Map<Integer, String> map) {
this.map = map;
}
@Override
public void run() {
for (int i = 1; i <= 30; i++) {
//String result = map.put(i, Thread.currentThread().getName());
String result = map.putIfAbsent(i, Thread.currentThread().getName());
}
}
}
|
3. Safe
package safe.demo;
import java.util.concurrent.ConcurrentHashMap;
import safe.threads.Safe_ConcurrentHashMap_NoSynchronize_UsingPutIfAbsent;
public class Safe_ConcurrentHashMap {
private ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<Integer, String>(
80, 0.5f, 200);
// private Map<Integer, String> map = new HashMap<Integer, String>();
public static void main(String[] args) throws InterruptedException {
for (int j = 1; j < 100; j++) {
Safe_ConcurrentHashMap demoClass = new Safe_ConcurrentHashMap();
for (int i = 0; i < 5; i++) {
new Thread(
new Safe_ConcurrentHashMap_NoSynchronize_UsingPutIfAbsent(demoClass.map))
.start();
}
Thread.currentThread().sleep(10);
System.out.println(demoClass.map.keySet());
demoClass.map.clear();
}
}
}
|
3. Safe cont.
package safe.threads;
import java.util.Map;
/**
* Thread safe hashmap usage through the use of 'synchronized' keyword based on:
* http://lovehasija.com/2012/08/16/who-said-java-hashtable-is-thread-safe/
*
* @author sshakil
*/
public class Safe_Hashtable_Synchronized_UsingContainsKey implements Runnable {
private Map<Integer, String> map = null;
public Safe_Hashtable_Synchronized_UsingContainsKey(Map<Integer, String> map) {
this.map = map;
}
@Override
public void run() {
for (int i = 1; i <= 30; i++) {
synchronized (map) {
if (!map.containsKey(i)) {
if (map.containsKey(i)) {
System.out.println("TRUE?");
try {
throw new Exception("this can't be!");
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()
+ "\tInserting new, Key exists: "
+ map.containsKey(i));
map.put(i, Thread.currentThread().getName());
}
}
}
}
} |
No comments:
Post a Comment