Java並發06---ThreadLocal類以及應用技巧__Java

來源:互聯網
上載者:User

                                                                                Java並發06---ThreadLocal類以及應用技巧

上一節總結了一下,線程範圍內的資料共用問題,即定義一個Map,將當前線程名稱和線程中的資料以索引值對的形式存到Map中,然後在當前線程中使用資料的時候就可以根據當前線程名稱從Map中拿到當前線程中的資料,這樣就可以做到不同線程之間資料互不干擾。其實ThreadLocal類就是給我們提供了這個解決方案,所以我們完全可以用ThreadLocal來完成線程範圍內資料的共用。

public class ThreadScopeShareData {    //定義一個ThreadLocal    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();    public static void main(String[] args) {        for(int i = 0; i < 2; i ++) {            new Thread(new Runnable() {                @Override                public void run() {                    int data = new Random().nextInt();                    System.out.println(Thread.currentThread().getName() + " has put a data: " + data);                    threadLocal.set(data);//直接往threadLocal裡面裡面扔資料即可                    new TestA().getData();                    new TestB().getData();                }            }).start();        }    }    static class TestA {        public void getData() {            System.out.println("A get data from " + Thread.currentThread().getName() + ": " + threadLocal.get());//直接取,不用什麼關鍵字,它直接從當前線程中取        }    }    static class TestB {        public void getData() {            System.out.println("B get data from " + Thread.currentThread().getName() + ": " + threadLocal.get());//直接取,不用什麼關鍵字,它直接從當前線程中取        }    }}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

  結合上一節的代碼,可以看出,其實ThreadLocal就相當於一個Map,只不過我們不需要設定key了,它預設就是當前的Thread,往裡面放資料,直接set即可,取資料,直接get即可,很方便,就不用Map一個個存了,但是問題來了,ThreadLocal雖然存取方便,但是get()方法中根本沒有參數,也就是說我們只能往ThreadLocal中放一個資料,多了就不行了,那麼該如何解決這個問題呢。 
  很明顯,ThreadLocal是個容器,且只能存一下,那麼如果有多個資料,我們可以定義一個類,把資料都封裝到這個類中,然後扔到ThreadLocal中,用的時候取這個類,再從類中去我們想要的資料即可。 
好,現在有兩個線程,每個線程都要操作各自的資料,而且資料有兩個:名字和年齡。根據上面的思路,寫一個demo,如下:

public class ThreadScopeShareData {    private static ThreadLocal<User> threadLocal = new ThreadLocal<User>();    public static void main(String[] args) {        for(int i = 0; i < 2; i ++) {//開啟兩個線程            new Thread(new Runnable() {                @Override                public void run() {                    int data = new Random().nextInt();                    System.out.println(Thread.currentThread().getName() + " has put a data: " + data);                    //每個線程中維護一個User,User中儲存了name和age                    User user = new User();                    user.setName("name" + data);                    user.setAge(data);                    threadLocal.set(user); //向當前線程中存入user對象                    new TestA().getData();                    new TestB().getData();                }            }).start();        }    }    static class TestA {        public void getData() {            User user = threadLocal.get();//從當前線程中取出user對象            System.out.println("A get data from " + Thread.currentThread().getName() + ": "                     + user.getName() + "," + user.getAge());        }    }    static class TestB {        public void getData() {            User user = threadLocal.get();//從當前線程中取出user對象            System.out.println("B get data from " + Thread.currentThread().getName() + ": "                     + user.getName() + "," + user.getAge());        }    }}//定義一個User類來儲存姓名和年齡class User {    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;    }   }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

  這樣進行一下封裝就可以實現多個資料的儲存了,但是上面這個程式是不太好的,原因很明顯,線上程中,我要自己new一個對象,然後對其進行操作,最後還得把這個對象扔到當前線程中。這不太符合設計的思路,設計的思路應該是這樣的,不能讓使用者自己去new啊,如果有個類似於getThreadInstance()的方法,使用者想要從ThreadLocal中拿什麼對象就用該對象去調用這個getThreadInstance()方法多好,這樣拿到的永遠都是本線程範圍內的對象了。 
  這讓我想到了學習JDBC的時候,從ThreadLocal中拿connection時的做法了,如果當前ThreadLocal中有就拿出來,沒有就產生一個,這跟這裡的需求是一樣的,我想要一個User,那我應該用User去調用getThreadLInstance()方法擷取本線程中的一個User對象,如果有就拿,如果沒有就產生一個。完全一樣的思路。這個設計跟單例的模式有點像,這裡說有點像不是本質上像,是代碼結構很像。先看一下簡單的單例模式代碼結構:

public class Singleton {    private static Singleton instance = null;    private Singleton() {//私人構造方法阻止外界new            }    public static synchronized Singleton getInstance() {  //提供一個公用方法返回給外界一個單例的執行個體        if (instance == null) {  //如果沒有執行個體            instance = new Singleton();  //就新new一個        }          return instance;  //返回該執行個體    } }
1 2 3 4 5 6 7 8 9 10 11

  這是懶漢式單例模式的代碼結構,我門完全可以效仿該思路去設計一個從當前線程中拿User的辦法,所以將程式修改如下:

public class ThreadScopeShareData {//不需要在外面定義threadLocal了,放到User類中了//  private static ThreadLocal<User> threadLocal = new ThreadLocal<User>();    public static void main(String[] args) {        for(int i = 0; i < 2; i ++) {            new Thread(new Runnable() {                @Override                public void run() {                    int data = new Random().nextInt();                    System.out.println(Thread.currentThread().getName() + " has put a data: " + data);                    //這裡直接用User去調用getThreadLocal這個靜態方法擷取本線程範圍內的一個User對象                    //這裡就優雅多了,我完全不用關心如何去拿該線程中的對象,如何把對象放到threadLocal中                    //我只要拿就行,而且拿出來的肯定就是當前線程中的對象,原因看下面User類中的設計                    User.getThreadInstance().setName("name" + data);                    User.getThreadInstance().setAge(data);                    new TestA().getData();                    new TestB().getData();                }            }).start();        }    }    static class TestA {        public void getData() {            //還是調用這個靜態方法拿,因為剛剛已經拿過一次了,threadLocal中已經有了            User user = User.getThreadInstance();            System.out.println("A get data from " + Thread.currentThread().getName() + ": "                     + user.getName() + "," + user.getAge());        }    }    static class TestB {        public void getData() {            User user = User.getThreadInstance();            System.out.println("A get data from " + Thread.currentThread().getName() + ": "                     + user.getName() + "," + user.getAge());        }    }}class User {    private User() {}    private static ThreadLocal<User> threadLocal = new ThreadLocal<User>();    //注意,這不是單例,每個線程都可以new,所以不用synchronized,    //但是每個threadLocal中是單例的,因為有了的話就不會再new了    public static /*synchronized*/ User getThreadInstance() {        User instance = threadLocal.get(); //先從當前threadLocal中拿        if(instance == null) {            instance = new User();            threadLocal.set(instance);//如果沒有就新new一個放到threadLocal中        }        return instance; //向外返回該User    }    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) {        

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.