static在所属环境中,所有线程共享同一份数据,无论哪个线程调用,结构都是一致的。如果是一个普通的的变量会怎样?
案例一、线程内的运行的结果不一致
public class NewThreadOne {
private int num = 0; //随机数
public static void main(String[] args) {
NewThreadOne newThreadOne = new NewThreadOne();
for (int i = 0; i < 2; i++) { //两个线程产生数据,并且调用A,B的获取数据的方法
new Thread(new Runnable() {
@Override
public void run() {
int num = (int) (Math.random() * 10000);
System.out.println(Thread.currentThread() + "add num is=" + num);
newThreadOne.setNum(num);
A a = newThreadOne.new A();
a.getData();
B b = newThreadOne.new B();
b.getData();
}
}).start();
}
}
public static void main(String[] args) {
NewThreadOne newThreadOne = new NewThreadOne();
for (int i = 0; i < 2; i++) { //两个线程产生数据,并且调用A,B的获取数据的方法
new Thread(new Runnable() {
@Override
public void run() {
int num = (int) (Math.random() * 10000);
System.out.println(Thread.currentThread() + "add num is=" + num);
newThreadOne.setNum(num);
A a = newThreadOne.new A();
a.getData();
B b = newThreadOne.new B();
b.getData();
}
}).start();
}
}
//两个对象都可以获取数据
class A {
public void getData() {
System.out.println("the class A ," + Thread.currentThread() + ",getData is=" + getNum());
}
}
class B {
public void getData() {
System.out.println("the class B ," + Thread.currentThread() + ",getData is=" + getNum());
}
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
获取数据的经过如下:
Thread[Thread-0,5,main]add num is=4253
Thread[Thread-1,5,main]add num is=2030
the class A ,Thread[Thread-0,5,main],getData is=2030
the class A ,Thread[Thread-1,5,main],getData is=2030
the class B ,Thread[Thread-1,5,main],getData is=2030
the class B ,Thread[Thread-0,5,main],getData is=2030
结果可见,线程内的数据数据没有共享。怎么让各现场的数据都是独立的呢?
public class NewThreadOne {
private int num = 0;
Map<Thread, Integer> map=new HashMap<>();
public static void main(String[] args) {
NewThreadOne newThreadOne = new NewThreadOne();
for(int i=0;i<2;i++){
new Thread(new Runnable() {
@Override
public void run() {
int num = (int) (Math.random() * 10000);
newThreadOne.map.put(Thread.currentThread(), num);
System.out.println(Thread.currentThread()+"add num is="+num);
newThreadOne.setNum(num);
A a=newThreadOne.new A();
a.getData();
B b=newThreadOne.new B();
b.getData();
}
}).start();
}
}
class A {
public void getData() {
System.out.println("the class A ," + Thread.currentThread() + ",getData is=" + map.get(Thread.currentThread()));
}
}
class B {
public void getData() {
System.out.println("the class B ," + Thread.currentThread() + ",getData is=" + map.get(Thread.currentThread()));
}
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
Map<Thread, Integer> map=new HashMap<>();
public static void main(String[] args) {
NewThreadOne newThreadOne = new NewThreadOne();
for(int i=0;i<2;i++){
new Thread(new Runnable() {
@Override
public void run() {
int num = (int) (Math.random() * 10000);
newThreadOne.map.put(Thread.currentThread(), num);
System.out.println(Thread.currentThread()+"add num is="+num);
newThreadOne.setNum(num);
A a=newThreadOne.new A();
a.getData();
B b=newThreadOne.new B();
b.getData();
}
}).start();
}
}
class A {
public void getData() {
System.out.println("the class A ," + Thread.currentThread() + ",getData is=" + map.get(Thread.currentThread()));
}
}
class B {
public void getData() {
System.out.println("the class B ," + Thread.currentThread() + ",getData is=" + map.get(Thread.currentThread()));
}
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
各自的结果从当前线程中拿到数据,实现线程内的数据共享。案例:在数据库中例如银行实现转账,线程内转入,转出在同一个线程共享区域。最终结果和线程外共享。
2、使用threadlocal实现
threadlocal提供了set(T t) 和get()方法,set的结果和当前的线程绑定,set代码如下:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
get也是从当前线程中取出,和上面的事例原理想象,如果案例中代码使用threadlocal会很方便代码如下:
package com.thread;
public class NewThreadOne {
private int num = 0;
ThreadLocal<Integer> local=new ThreadLocal<>();
public static void main(String[] args) {
NewThreadOne newThreadOne = new NewThreadOne();
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int num = (int) (Math.random() * 10000);
newThreadOne.local.set(num);
System.out.println(Thread.currentThread() + "add num is=" + num);
newThreadOne.setNum(num);
A a = newThreadOne.new A();
a.getData();
B b = newThreadOne.new B();
b.getData();
}
}).start();
}
}
class A {
public void getData() {
System.out.println(
"the class A ," + Thread.currentThread() + ",getData is=" + local.get());
}
}
class B {
public void getData() {
System.out.println(
"the class B ," + Thread.currentThread() + ",getData is=" + local.get());
}
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
经测试结果一致。
进一步升级:如下的代码把threadlocal封装到线程共享业务里面更合理。
public class NewThreadOne {
private int num = 0;
public static void main(String[] args) {
//
NewThreadOne newThreadOne = new NewThreadOne();
//
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int num = (int) (Math.random() * 10000);
Person p = Person.getInstance();
p.setName("zkk" + num);
p.setAge(num);
//
System.out.println(Thread.currentThread() + "add num is=" + num);
newThreadOne.setNum(num);
//
A a = newThreadOne.new A();
a.getData();
//
B b = newThreadOne.new B();
b.getData();
}
}).start();
}
}
class A {
public void getData() {
System.out.println("the class A ," + Thread.currentThread() + ",getData is=" + Person.local.get().getName());
}
}
class B {
public void getData() {
System.out.println("the class B ," + Thread.currentThread() + ",getData is=" + Person.local.get().getName());
}
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
class Person {
static ThreadLocal<Person> local = new ThreadLocal<>();
private Person() {};
private static Person person = null;
//
public synchronized static Person getInstance() {
//
if (local.get() == null) {
person = new Person();
local.set(person);
}
return local.get();
}
//
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
经典案例
public class ConcurrentDateUtil {
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static Date parse(String dateStr) throws ParseException {
return threadLocal.get().parse(dateStr);
}
public static String format(Date date) {
return threadLocal.get().format(date);
}
}
或
public class ThreadLocalDateUtil {
private static final String date_format = "yyyy-MM-dd HH:mm:ss";
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>();
public static DateFormat getDateFormat()
{
DateFormat df = threadLocal.get();
if(df==null){
df = new SimpleDateFormat(date_format);
threadLocal.set(df);
}
return df;
}
public static String formatDate(Date date) throws ParseException {
return getDateFormat().format(date);
}
public static Date parse(String strDate) throws ParseException {
return getDateFormat().parse(strDate);
}
}