Java多線程併發09——如何實現線程間與線程內數據共享

本文將爲各位帶來 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(線程的一個屬性)

  1. 每個線程中都有一個自己的 ThreadLocalMap 類對象,可以將線程自己的對象保持到其中,各管各的,線程可以正確的訪問到自己的對象;
  2. 將一個共用的 ThreadLocal 靜態實例作爲 key,將不同對象的引用保存到不同線程的 ThreadLocalMap 中,然後在線程執行的各處通過這個靜態 ThreadLocal 實例的 get()方法取得自己線程保存的那個對象,避免了將這個對象作爲參數傳遞的麻煩;
  3. 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多線程併發08——鎖在Java中的應用

Java多線程併發07——鎖在Java中的實現

Java多線程併發06——CAS與AQS

Java多線程併發05——那麼多的鎖你都瞭解了嗎

Java多線程併發04——合理使用線程池

Java多線程併發03——什麼是線程上下文,線程是如何調度的

Java多線程併發02——線程的生命週期與常用方法,你都掌握了嗎

Java多線程併發01——線程的創建與終止,你會幾種方式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章