线程范围内数据共享
package multiThread;
import java.util.concurrent.ThreadLocalRandom;
public class ThreadScopeSharedData {
private static int data = 0;
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
public void run() {
data = ThreadLocalRandom.current().nextInt();
System.out.println(Thread.currentThread().getName() +
" has put data " + data);
new A().get();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new B().get();
}
}).start();
Thread.sleep(1000);
}
}
static class A {
public void get() {
System.out.println(
Thread.currentThread().getName() + " A : " + data);
}
}
static class B {
public void get() {
System.out.println(
Thread.currentThread().getName() + " B : " + data);
}
}
}
/*
Thread-0 has put data -1657334192
Thread-0 A : -1657334192
Thread-1 has put data -2108767251
Thread-1 A : -2108767251
Thread-0 B : -2108767251 <-------
Thread-1 B : -2108767251
*/
加入map,将产生的数据与当前线程绑定后放入map
public class ThreadScopeSharedData {
private static int data = 0;
private static Map<Thread, Integer> map = new HashMap<>();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
public void run() {
data = ThreadLocalRandom.current().nextInt();
map.put(Thread.currentThread(), data);
System.out.println(Thread.currentThread().getName() +
" has put data " + data);
new A().get();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new B().get();
}
}).start();
Thread.sleep(1000);
}
System.out.println("now the data is " + data);
}
static class A {
public void get() {
int data = map.get(Thread.currentThread());
System.out.println(
Thread.currentThread().getName() + " A : " + data);
}
}
static class B {
public void get() {
int data = map.get(Thread.currentThread());
System.out.println(
Thread.currentThread().getName() + " B : " + data);
}
}
}
/*
Thread-0 has put data -563709465
Thread-0 A : -563709465
Thread-1 has put data -393013760
Thread-1 A : -393013760
Thread-0 B : -563709465 <-----------
Thread-1 B : -393013760
now the data is -393013760
*/
在单线程应用程序中可能会维持一个全局的数据库连接,并在程序启动的时候初始化这个连接对象,从而避免在调用每个方法时都要传递一个Connection对象。由于JDBC的连接对象不一定是线程安全的,因此,当多线程应用程序在没有协同的情况下使用全局变量时,就不是线程安全的。通过将JDBC的连接保存到ThreadLocal对象中,每个线程都会拥有属于自己的连接。
这种思想用在JavaEE里面的一个场景描述如下:银行转账业务,A,B,C用的是同一个Connection的不同线程开启一个事务,将事务上下文保存在静态的ThreadLocal对象中,可以很容易地实现这个功能。
import java.util.concurrent.ThreadLocalRandom;
public class ThreadLocalTest {
private static ThreadLocal<Integer> x = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
public void run() {
int data = ThreadLocalRandom.current().nextInt();
x.set(data);
System.out.println(Thread.currentThread().getName() +
" has put data " + data);
new A().get();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new B().get();
}
}).start();
Thread.sleep(1000);
}
System.out.println("now the data is " + x.get());
}
static class A {
public void get() {
int data = x.get();
System.out.println(
Thread.currentThread().getName() + " A : " + data);
}
}
static class B {
public void get() {
int data = x.get();
System.out.println(
Thread.currentThread().getName() + " B : " + data);
}
}
}
/*
Thread-0 has put data -1154183505
Thread-0 A : -1154183505
Thread-1 has put data 583017524
Thread-1 A : 583017524
Thread-0 B : -1154183505
Thread-1 B : 583017524 <-----------
now the data is null <-----------主线程没有设定这个变量
*/
一个ThreadLocal只能代表一个变量,放一个数据,如果有多个数据,则需要将多个数据封装成一个对象放入ThreadLocal中。
import java.util.concurrent.ThreadLocalRandom;
public class ThreadLocalObjectTest {
private static ThreadLocal<Integer> x = new ThreadLocal<>();
private static ThreadLocal<MyThreadLocalData> myThreadLocaldata = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
public void run() {
int data = ThreadLocalRandom.current().nextInt();
x.set(data);
System.out.println(Thread.currentThread().getName() +
" has put data " + data);
MyThreadLocalData myData = new MyThreadLocalData();
myData.setName("name" + data + ", age ");
myData.setAge(data);
myThreadLocaldata.set(myData);
new A().get();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new B().get();
}
}).start();
Thread.sleep(1000);
}
}
static class A {
public void get() {
int data = x.get();
System.out.println(
Thread.currentThread().getName() + " A : " + data);
MyThreadLocalData myData = myThreadLocaldata.get();
System.out.println(
Thread.currentThread().getName() + " A : " + myData.getName() + myData.getAge());
}
}
static class B {
public void get() {
int data = x.get();
System.out.println(
Thread.currentThread().getName() + " B : " + data);
MyThreadLocalData myData = myThreadLocaldata.get();
System.out.println(
Thread.currentThread().getName() + " B : " + myData.getName() + myData.getAge());
}
}
}
class MyThreadLocalData {
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;
}
}
/*
Thread-0 has put data -73677040
Thread-0 A : -73677040
Thread-0 A : name-73677040, age -73677040
Thread-1 has put data 128646006
Thread-1 A : 128646006
Thread-1 A : name128646006, age 128646006
Thread-0 B : -73677040
Thread-0 B : name-73677040, age -73677040
Thread-1 B : 128646006
Thread-1 B : name128646006, age 128646006
*/
这样的代码太丑陋,所以对上面的例子程序进行改进。
import java.util.concurrent.ThreadLocalRandom;
public class ThreadLocalSingleTest {
private static ThreadLocal<Integer> x = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
public void run() {
int data = ThreadLocalRandom.current().nextInt();
x.set(data);
System.out.println(Thread.currentThread().getName() +
" has put data " + data);
MyThreadLocalDataSingle.getThreadInstance().setName("name" + data + ", age ");
MyThreadLocalDataSingle.getThreadInstance().setAge(data);
new A().get();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new B().get();
}
}).start();
Thread.sleep(1000);
}
}
static class A {
public void get() {
int data = x.get();
System.out.println(
Thread.currentThread().getName() + " A : " + data);
MyThreadLocalDataSingle myData = MyThreadLocalDataSingle.getThreadInstance();
System.out.println(
Thread.currentThread().getName() + " A : " + myData.getName() + myData.getAge());
}
}
static class B {
public void get() {
int data = x.get();
System.out.println(
Thread.currentThread().getName() + " B : " + data);
MyThreadLocalDataSingle myData = MyThreadLocalDataSingle.getThreadInstance();
System.out.println(
Thread.currentThread().getName() + " B : " + myData.getName() + myData.getAge());
}
}
}
class MyThreadLocalDataSingle {
private static ThreadLocal<MyThreadLocalDataSingle> map = new ThreadLocal<>();
private MyThreadLocalDataSingle() {}
// 仅仅和本线程有关,无需加synchronized关键字
public static /*synchronized*/ MyThreadLocalDataSingle getThreadInstance() {
MyThreadLocalDataSingle instance = map.get();
if (instance == null) {
instance = new MyThreadLocalDataSingle();
map.set(instance);
}
return instance;
}
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;
}
}
/*
Thread-0 has put data -1169633973
Thread-0 A : -1169633973
Thread-0 A : name-1169633973, age -1169633973
Thread-1 has put data 1917450343
Thread-1 A : 1917450343
Thread-1 A : name1917450343, age 1917450343
Thread-0 B : -1169633973
Thread-0 B : name-1169633973, age -1169633973
Thread-1 B : 1917450343
Thread-1 B : name1917450343, age 1917450343
*/
从概念上来看,可以讲ThreadLocal
视为包含了Map
对象,其中保存了特定于该线程的值,但是实现并非如此。这些特定于线程的值保存在Thread对象中,当线程终止后,这些值会作为垃圾回收。