HashMap、HashTable、ConcurrentHashMap
HashMap是线程不安全的,并发条件下不能使用HashMap,多线程环境下,HashMap会出现死锁(可以参考:)。HashTable是线程安全的,但是效率低下。HashTable使用synchronized来保证线程安全,一个线程在做put操作时,另外一个线程既不能put也不能get,因此竞争越激烈,效率越低。
因此,多线程环境下推荐使用ConcurrentHashMap,它使用的是锁分段技术。HashTable容器中只有一把锁,因此竞争激烈。ConcurrentHashMap容器中有多把锁,每一个锁管理容器的某一段。那么,多线程访问不同数据段的数据时,就不会存在锁竞争,从而可以有效的提升并发效率。当一个线程访问某一段数据的时候,其他段的数据仍然可以被其他线程访问。
内部结构大致如下:
按位与”。
2、ThreadLocal
ThreadLocal让变量只可以让一个线程来访问。因此,尽管有两个线程同时指向同一个ThreadLocal的引用,但是他们仍然只能访问自己的ThreadLocal变量域。
下面是一个完整的代码示例:
public class ThreadLocalExample {
public static class MyRunnable implements Runnable {
private ThreadLocal<Integer> threadLocal =
new ThreadLocal<Integer>();
@Override
public void run() {
threadLocal.set( (int) (Math.random() * 100D) );
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(threadLocal.get());
}
}
public static void main(String[] args) {
MyRunnable sharedRunnableInstance = new MyRunnable();
Thread thread1 = new Thread(sharedRunnableInstance);
Thread thread2 = new Thread(sharedRunnableInstance);
thread1.start();
thread2.start();
thread1.join(); //wait for thread 1 to terminate
thread2.join(); //wait for thread 2 to terminate
}
}
上面创建了两个线程共享一个MyRunnable实例。每个线程执行run()方法的时候,会给同一个ThreadLocal实例设置不同的值。如果调用set()方法的时候用synchronized关键字同步,而且不是一个ThreadLocal对象实例,那么第二个线程将会覆盖第一个线程所设置的值。
然而,由于是ThreadLocal对象,所以两个线程无法看到彼此的值。因此,可以设置或者获取不同的值。
下面是一个ThreadLocal运用的完整案例:
先把ThreadLocal进行封装,主要是在本地线程存储用户信息,客户端IP,服务名称等。
public class RequestContext {
public static final String USER = "user";
public static final String CLIENT_IP = "clientIp";
protected static ThreadLocal<Map<String, Object>> requestThreadLocal = new ThreadLocal<Map<String, Object>>();
private RequestContext() {
}
public static Map<String, Object> get() {
Map<String, Object> map = requestThreadLocal.get();
if (map == null) {
map = new HashMap<String, Object>();
requestThreadLocal.set(map);
}
return map;
}
public static void setUser(User user) {
get().put(USER, user);
}
public static User getUser() {
return (User) get().get(USER);
}
// 检测用户信息,如果存在则返回,不存在则抛出异常。
public static User checkAndGetUser() {
User user = getUser();
if (user == null) {
throw new BusinessException(RespCode.RESP_0057);
}
return user;
}
public static void setClientIp(String ip) {
get().put(CLIENT_IP, ip);
}
public static String getClientIp() {
return (String) get().get(CLIENT_IP);
}
}
然后可以利用spring的aop,在每次调用接口之前,先验证并存储用户信息和客户端IP。