一、volatile关键字
适合于只有一个线程写,多个线程读的场景,因为它只能确保可见性。
这样的场景我们可以再ConcurrentHahsMap里面体现的最好了,比如说内部类的 Node 类。可以参考一下;
二、ThreadLocal
线程变量。可以理解为是个map,类型 Map<Thread,Integer>
下面我们还是详细讲一下吧,现在发现我可以好好看一下这个了;
ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
1、ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。
2、ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值。
3、ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。
4、ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值。
看一个简单的例子:
package com.youyou.test.demotest;
public class MyThreadLocal {
private static final ThreadLocal<Object> THREAD_LOCAL = new InheritableThreadLocal<Object>(){
@Override
protected Object initialValue(){
System.out.println("调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!");
return null;
}
};
public static void main(String[] args)
{
new Thread(new MyIntegerTask("IntegerTask1")).start();
new Thread(new MyStringTask("StringTask1")).start();
new Thread(new MyIntegerTask("IntegerTask2")).start();
new Thread(new MyStringTask("StringTask2")).start();
}
public static class MyIntegerTask implements Runnable{
private String name;
MyIntegerTask(String name){this.name = name;}
@Override
public void run()
{
for (int i = 0; i < 5; i++) {
// ThreadLocal.get方法获取线程变量
if (null == MyThreadLocal.THREAD_LOCAL.get())
{
MyThreadLocal.THREAD_LOCAL.set(0);
}else {
int num = (Integer)MyThreadLocal.THREAD_LOCAL.get();
MyThreadLocal.THREAD_LOCAL.set(num + 1);
System.out.println("线程" + name + ": " + MyThreadLocal.THREAD_LOCAL.get());
if(i == 3)
{
MyThreadLocal.THREAD_LOCAL.remove();
}
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
public static class MyStringTask implements Runnable {
private String name;
MyStringTask(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
if (null == MyThreadLocal.THREAD_LOCAL.get()) {
MyThreadLocal.THREAD_LOCAL.set("a");
System.out.println("线程" + name + ": a");
} else {
String str = (String) MyThreadLocal.THREAD_LOCAL.get();
MyThreadLocal.THREAD_LOCAL.set(str + "a");
System.out.println("线程" + name + ": " + MyThreadLocal.THREAD_LOCAL.get());
}
try {
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
ThreadLocal的set和get方法代码:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
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();
}
但是也不是说 ThreadLocal 就是安全的了,我们看一下这个例子;
public class Son implements Cloneable{
public static void main(String[] args){
Son p=new Son();
System.out.println(p);
Thread t = new Thread(new Runnable(){
public void run(){
ThreadLocal<Son> threadLocal = new ThreadLocal<>();
System.out.println(threadLocal);
threadLocal.set(p);
System.out.println(threadLocal.get());
threadLocal.remove();
try {
threadLocal.set((Son) p.clone());
System.out.println(threadLocal.get());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println(threadLocal);
}});
t.start();
}
}
输出结果:
Son@7852e922
java.lang.ThreadLocal@3ffc8195
Son@7852e922
Son@313b781a
java.lang.ThreadLocal@3ffc8195
也就是如果把一个共享的对象直接保存到ThreadLocal中,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。 所以要在保存到ThreadLocal之前,通过克隆或者new来创建新的对象,然后再进行保存。
ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用。作用:提供一个线程内公共变量(比如本次请求的用户信息),减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度,或者为线程提供一个私有的变量副本,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
关于还有其他的好多信息需要在研究,可以参考下面的文章:(讲的还是可以的)
https://www.cnblogs.com/xzwblog/p/7227509.html
来源:oschina
链接:https://my.oschina.net/u/4274516/blog/3522202