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。