本文將為各位帶來有關 Java 線程內數據共享的方法。關注我的公眾號「Java面典」了解更多 Java 相關知識點。
線程間數據共享
Java 里面進行多線程通信的主要方式就是共享內存的方式,共享內存主要的關注點有兩個:可見性和有序性原子性。Java 內存模型(JMM)解決了可見性和有序性的問題,而鎖解決了原子性的問題,理想情況下我們希望做到“同步”和“互斥”。有以下常規實現方法:
將數據抽象成一個類
將數據抽象成一個類,並將對這個數據的操作作為這個類的方法,這么設計可以和容易做到同步,只要在方法上加”synchronized“。
public class MyData {
private int j = 0;
public synchronized void add() {
j++;
System.out.println("線程" + Thread.currentThread().getName() + "j 為:" + j);
}
public synchronized void dec() {
j--;
System.out.println("線程" + Thread.currentThread().getName() + "j 為:" + j);
}
public int getData() {
return j;
}
}
public class AddRunnable implements Runnable {
MyData data;
public AddRunnable(MyData data) {
this.data = data;
}
public void run() {
data.add();
}
}
public class DecRunnable implements Runnable {
MyData data;
public DecRunnable(MyData data) {
this.data = data;
}
public void run() {
data.dec();
}
public static void main(String[] args) {
MyData data = new MyData();
Runnable add = new AddRunnable(data);
Runnable dec = new DecRunnable(data);
for (int i = 0; i < 2; i++) {
new Thread(add).start();
new Thread(dec).start();
}
}
}
Runnable 對象作為一個類的內部類
將 Runnable 對象作為一個類的內部類,共享數據作為這個類的成員變量,每個線程對共享數據的操作方法也封裝在外部類,以便實現對數據的各個操作的同步和互斥,作為內部類的各個 Runnable 對象調用外部類的這些方法。
public class MyData {
private int j = 0;
public synchronized void add() {
j++;
System.out.println("線程" + Thread.currentThread().getName() + "j 為:" + j);
}
public synchronized void dec() {
j--;
System.out.println("線程" + Thread.currentThread().getName() + "j 為:" + j);
}
public int getData() {
return j;
}
}
public class TestThread {
public static void main(String[] args) {
final MyData data = new MyData();
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
public void run() {
data.add();
}
}).start();
new Thread(new Runnable() {
public void run() {
data.dec();
}
}).start();
}
}
}
線程內數據共享(ThreadLocal)
ThreadLocal 的作用是提供線程內的局部變量,這種變量在線程的生命周期內起作用,減少同一個線程內多個函數或者組件之間一些公共變量的傳遞的復雜度。
ThreadLocalMap(線程的一個屬性)
- 每個線程中都有一個自己的 ThreadLocalMap 類對象,可以將線程自己的對象保持到其中,各管各的,線程可以正確的訪問到自己的對象;
- 將一個共用的 ThreadLocal 靜態實例作為 key,將不同對象的引用保存到不同線程的 ThreadLocalMap 中,然后在線程執行的各處通過這個靜態 ThreadLocal 實例的 get()方法取得自己線程保存的那個對象,避免了將這個對象作為參數傳遞的麻煩;
- ThreadLocalMap 其實就是線程里面的一個屬性,它在 Thread 類中定義ThreadLocal.ThreadLocalMap threadLocals = null;
適用場景
最常見的 ThreadLocal 使用場景為用來解決數據庫連接、Session 管理等,實現線程內數據共享。
private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() throws InfrastructureException {
Session s = (Session) threadSession.get();
try {
if (s == null) {
s = getSessionFactory().openSession();
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}
多線程與並發系列推薦
Java多線程並發03——什么是線程上下文,線程是如何調度的