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(); } }
//两个对象都可以获取数据 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; } }
各自的结果从当前线程中拿到数据,实现线程内的数据共享。案例:在数据库中例如银行实现转账,线程内转入,转出在同一个线程共享区域。最终结果和线程外共享。
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; } }