天天看點

借助ThreadLocal實作多線程安全 | 帶你學《Java語言進階特性》之三十二

上一篇:教你使用UUID、Optional類 | 帶你學《Java語言進階特性》之三十一

本節結合消息發送案例簡單直白地向讀者展現了多線程模式下的線程安全問題,并向讀者介紹了ThreadLocal類及其實作線程安全的原理,講解了其使用方法。

【本節目标】

通過閱讀本節内容,你将了解到多線程模式下重複性業務的安全問題和ThreadLocal能夠實作線程安全的原理,學會使用ThreadLocal類來解決開發過程中的線程安全問題。

ThreadLocal類

在真正去了解ThreadLocal類作用的時候下面編寫一個簡單的程式做一個先期的分析。

範例:現在定義這樣一個結構

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Message msg=new Message();   //執行個體化消息主體對象
        msg.setInfo("www.mldn.cn");     //設定要發送的内容
        Channel.setMessage(msg);    //設定要發送的消息
        Channel.send();    //發送消息
    }
}
class Channel {  //消息的發送通道
    private Channel(){}
    public static Message message;
    public static void setMessage(Message m) {
        message = m;
    }
    public static void send() {   //發送消息
        System.out.println("【消息發送】" + message.getInfo());
    }   //【消息發送】www.mldn.cn
}
class Message {    //要發送的消息體
    private String info;
    public void setInfo(String info) {
        this.info = info;
    }
    public String getInfo() {
        return info;
    }
}           
借助ThreadLocal實作多線程安全 | 帶你學《Java語言進階特性》之三十二

對于目前的程式實際上采用的是一種單線程的模式來進行處理的;那麼如果在多線程的狀态下能否實作完全一緻的操作效果呢?為此将啟動三個線程進行處理。

範例:多線程的影響

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        new Thread(()->{
            Message msg=new Message();//執行個體化消息主體對象
            msg.setInfo("第一個線程的消息");//設定要發送的内容
            Channel.setMessage(msg);//設定要發送的消息
            Channel.send();//發送消息
        },"消息發送者A").start();
        new Thread(()->{
            Message msg=new Message();//執行個體化消息主體對象
            msg.setInfo("第二個線程的消息");//設定要發送的内容
            Channel.setMessage(msg);//設定要發送的消息
            Channel.send();//發送消息
        },"消息發送者B").start();
        new Thread(()->{
            Message msg=new Message();//執行個體化消息主體對象
            msg.setInfo("第三個線程的消息");//設定要發送的内容
            Channel.setMessage(msg);//設定要發送的消息
            Channel.send();//發送消息
        },"消息發送者C").start();
    }
}
class Channel {//消息的發送通道
    private Channel(){}
    public static Message message;
    public static void setMessage(Message m) {
        message = m;
    }
    public static void send() {//發送消息
        System.out.println("【"+Thread.currentThread().getName()+"、消息發送】" + message.getInfo());
    }
}
class Message {//要發送的消息體
    private String info;
    public void setInfo(String info) {
        this.info = info;
    }
    public String getInfo() {
        return info;
    }
}
/**
 * 【消息發送者A、消息發送】第二個線程的消息
 * 【消息發送者B、消息發送】第二個線程的消息
 * 【消息發送者C、消息發送】第三個線程的消息
 */           

這個時候消息的處理産生了影響。

借助ThreadLocal實作多線程安全 | 帶你學《Java語言進階特性》之三十二

考慮多線程ThreadLocal類

在保持Channel(所有發送的通道)核心結構不改變的情況下,需要考慮到每個線程的獨立操作問題。那麼在這樣的情況下,發現對于Channel類而言,除了要保留有發送的消息之外,還應該多存放一個每一個線程的标記(目前線程),這個時候可以通過ThreadLocal類來存放資料。在ThreadLocal類裡面提供有如下操作方法:

  • 構造方法:public ThreadLocal();
  • 設定資料:public void set(T value);
  • 取出資料:public T get();
  • 删除資料:public void remove();
借助ThreadLocal實作多線程安全 | 帶你學《Java語言進階特性》之三十二

解決同步問題

範例:解決線程同步問題

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        new Thread(()->{
            Message msg=new Message();//執行個體化消息主體對象
            msg.setInfo("第一個線程的消息");//設定要發送的内容
            Channel.setMessage(msg);//設定要發送的消息
            Channel.send();//發送消息
        },"消息發送者A").start();
        new Thread(()->{
            Message msg=new Message();//執行個體化消息主體對象
            msg.setInfo("第二個線程的消息");//設定要發送的内容
            Channel.setMessage(msg);//設定要發送的消息
            Channel.send();//發送消息
        },"消息發送者B").start();
        new Thread(()->{
            Message msg=new Message();//執行個體化消息主體對象
            msg.setInfo("第三個線程的消息");//設定要發送的内容
            Channel.setMessage(msg);//設定要發送的消息
            Channel.send();//發送消息
        },"消息發送者C").start();
    }
}
class Channel {   //消息的發送通道
   private Channel(){}
   public static final ThreadLocal<Message> THREADLOCAL=new ThreadLocal<Message>();
   public static void setMessage(Message m) {
       THREADLOCAL.set(m);    //向ThreadLocal中儲存資料
   }
   public static void send() {  //發送消息
       Message message=THREADLOCAL.get();
       System.out.println("【"+Thread.currentThread().getName()+"、消息發送】" + THREADLOCAL.get().getInfo());
   }
}
class Message {//要發送的消息體
    private String info;
    public void setInfo(String info) {
        this.info = info;
    }
    public String getInfo() {
        return info;
    }
}
/**
* 【消息發送者B、消息發送】第二個線程的消息
* 【消息發送者C、消息發送】第三個線程的消息
* 【消息發送者A、消息發送】第一個線程的消息
*/           

每一個線程通過ThreadLocal隻允許儲存一個資料。

想學習更多的Java的課程嗎?從小白到大神,從入門到精通,更多精彩不容錯過!免費為您提供更多的學習資源。

本内容視訊來源于

阿裡雲大學 下一篇:手把手教你建立定時任務 | 帶你學《Java語言進階特性》之三十三 更多Java面向對象程式設計文章檢視此處