Java 线程安全
1. 什么是线程安全?
可变资源(内存)间线程共享。
2. 如何实现线程安全?
- 不共享资源;
- 共享不可变资源;
- 共享可变资源:
- 可见性;
- 操作原子性;
- 禁止重排序;
1. 不共享资源
1. 可重入函数:函数体内部不涉及任何外部变量;
// 可重入函数 public static int addTwo(int num) { return num + 3; }
2. 使用ThreadLoacl:
final static ThreadLocal<String> mThreadLocal = new ThreadLocal<>(); public static void test() { Runnable runnable = new Runnable() { @Override public void run() { mThreadLocal.set(UUID.randomUUID().toString()); System.out.println(Thread.currentThread().getName() + " -- token : " + mThreadLocal.get()); } }; for (int idx = 0; idx < 3; idx++) { Thread thread = new Thread(runnable); thread.start(); } }
上面的结果就是每个线程都有自己的副本,输出的值也不相同。
ThreadLocal作用及实现原理:
ThreadLocal作用是用于线程间数据隔离,ThreadLocal是线程内部数据存储类,通过ThreadLocal将数据存储在指定线程中,存储后只能通过指定线程获取数据,其它线程是获取不到数据的。ThreadLocal是一个Map(ThreadLocalMap),Key是线程对象Thread.currentThread,值为存储的数据,在Hander的Looper类中的ThreadLocal存储的是Looper对象。
在Looper中ThreadLocal是静态常量存储于静态存储区,线程中工作内存中使用的是ThreadLocal的主内存的副本,但是由于ThreadLocal是一个Map对象,Key又是线程(Thread.currentThread),只能通过指定线程获取ThreadLocal中的数据,所以,做到了线程间数据隔离互不影响。
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } /** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
通过ThreadLocal源码中get()和set()方法,操作的变量是ThreadLocalMap。
ThreadLocalMap的创建:
/** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; } /** * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the map */ void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
ThreadLocalMap就是一个Map,而向Map中存储数据的key是Thread的this(map.set(this, value);),就是将值存储到线程上的,就是通过Map将thread的this和value绑定。
ThreadLocal使用建议:
-
- 声明为全局静态final变量;
- 避免存储大量对象;
- 使用完后及时移除对象;
2. 共享不可变资源
使用final关键字
3. 共享可变资源
1. 禁止重排序特性;
2. 可见性:
使用final和volatile关键字:
final:1. 修饰变量不可被修改;2. 修饰类不可被继承;3. 修饰方法不可被覆写;4. 禁止重排序特性;
volatile:1. 可见性 -- 多线程操作时,其中一个线程修改了volatile修饰的变量,会通知其它线程变量改变;2. 禁止重排序;
PS:每个线程都有自己的工作内存,工作内存将主内存变量copy到工作内存后,再执行操作。所以,导致了多线程时变量数据不一致性。
2. 加锁:锁释放后强制将缓存数据刷新到主内存。
3. 操作原子性:加锁。