夜光:Java成神之路(九)擅長的語言

夜光序言:

 

 

學會去看透一切的謎題.
Learning to see the puzzle in everything.
它們無處不在.
They're everywhere.
一旦你開始尋找,就不可能停下了
Once you start looking, it's impossible to stop.
湊巧的是 人們 以及可以體現他們所作所爲的謊言和錯覺,是最引人入勝的謎題。
It just so happens people,and delusions that inform everything they do,tend to be the most fascinating puzz

《福爾摩斯:基本演繹法》

 

 

 

 
 
正文:
 
                                              以道御術 / 以術識道



1. ThreadLocal 的使用方式

 

(1) 在關聯數據類中創建 private static ThreadLocal

 
在下面的類中,私有靜態 ThreadLocal 實例(serialNum)爲調用該類的靜態 SerialNum.get() 方法的每個線程維護了一個“序列號”,該方法將返回當前線程的序列號。
 
 
(線程的序列號是在第一次調用 SerialNum.get() 時分配的,並在後續調用中不會更改。)

 

另一個例子,也是私有靜態 ThreadLocal 實例:

package com.hy.多線程高併發;

public class ThreadContext {


    private String userId;
    private Long transactionId;


    private static ThreadLocal threadLocal = new ThreadLocal() {
        @Override
        protected ThreadContext initialValue() {
            return new ThreadContext();
        }
    };

    public static ThreadContext get() {
        return (ThreadContext) threadLocal.get(); //強轉一下
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public Long getTransactionId() {
        return transactionId;
    }

    public void setTransactionId(Long transactionId) {
        this.transactionId = transactionId;
    }
}
補充:在 JDK 的 API 對 ThreadLocal 私有化的說明。並舉例‘線程唯一標識符’
 
UniqueThreadIdGenerator ,大家學習是可以結合官方 API 來學習。

 

 


2. 在 Util 類中創建 ThreadLocal

 

這是上面用法的擴展,即把 ThreadLocal 的創建放到工具類中。

package com.hy.多線程高併發;


public class HibernateUtil {
    
    private static Log log = LogFactory.getLog(HibernateUtil.class);
    private static final SessionFactory sessionFactory; //定義 SessionFactory

    static {
        try {
// 通過默認配置文件 hibernate.cfg.xml 創建 SessionFactory
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            log.error("初始化 SessionFactory 失敗!", ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    //創建線程局部變量 session,用來保存 Hibernate 的 Session
    public static final ThreadLocal session = new ThreadLocal();

    /**
     * 獲取當前線程中的 Session
     *
     * @return Session
     * @throws HibernateException
     */
    public static Session currentSession() throws HibernateException {
        Session s = (Session) session.get();
// 如果 Session 還沒有打開,則新開一個 Session
        if (s == null) {
            s = sessionFactory.openSession();
            session.set(s); //將新開的 Session 保存到線程局部變量中
        }
        return s;
    }

    public static void closeSession() throws HibernateException {
//獲取線程局部變量,並強制轉換爲 Session 類型
        Session s = (Session) session.get();
        session.set(null);
        if (s != null)
            s.close();
    }
}

 


3. 在 Runnable 中創建 ThreadLocal

 

在線程類內部創建 ThreadLocal,基本步驟如下:

 
①、在多線程的類(如 ThreadDemo 類)中,創建一個 ThreadLocal 對象 threadXxx,用來保存線程間需要隔離處理的對象 xxx。
 
 
②、在 ThreadDemo 類中,創建一個獲取要隔離訪問的數據的方法 getXxx(),在方法中判斷,若ThreadLocal 對象爲 null 時候,應該 new()一個隔離訪問類型的對象,並強制轉換爲要應用的類型
 
 
③、在 ThreadDemo 類的 run()方法中,通過調用 getXxx()方法獲取要操作的數據,這樣可以保證每個線程對應一個數據對象,在任何時刻都操作的是這個對象。
 
 
package com.hy.多線程高併發;

import java.util.Random;

public class ThreadLocalTest implements Runnable{

    ThreadLocal<Studen> studenThreadLocal = new ThreadLocal<Studen>();

    @Override
    public void run() {
        String currentThreadName = Thread.currentThread().getName();
        System.out.println(currentThreadName + " is running...");
        Random random = new Random();
        int age = random.nextInt(100);
        System.out.println(currentThreadName + " is set age: " + age);
        Studen studen = getStudent();
        //通過這個方法,爲每個線程都獨立的 new 一個 student 對象,每個線程的的
        //student 對象都可以設置不同的值
        studen.setAge(age);
        System.out.println(currentThreadName + " is first get age: " + studen.getAge());
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println( currentThreadName + " is second get age: " + studen.getAge());
    }
    private Studen getStudent() {
        Studen studen = studenThreadLocal.get();
        if (null == studen) {
            studen = new Studen();
            studenThreadLocal.set(studen);
        }
        return studen;
    }
    public static void main(String[] args) {
        ThreadLocalTest t = new ThreadLocalTest();
        Thread t1 = new Thread(t,"Thread A");
        Thread t2 = new Thread(t,"Thread B");
        t1.start();
        t2.start();
    }
}


class Studen {
    int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

( 5 ) 多線程共享數據

 

在 Java 傳統線程機制中的共享數據方式,大致可以簡單分兩種情況:

 
多個線程行爲一致,共同操作一個數據源
 
也就是每個線程執行的代碼相同,可以使用同一個 Runnable 對象,這個 Runnable 對象中有那個共享數據,例如,賣票系統就可以這麼做。
 
 
多個線程行爲不一致,共同操作一個數據源。也就是每個線程執行的代碼不同這時候需要用不同的Runnable 對象。
 
例如,銀行存取款。
 
 
下面我們通過兩個示例代碼來分別說明這兩種方式。
 

 

1. 多個線程行爲一致共同操作一個數據

 
如果每個線程執行的代碼相同可以使用同一個 Runnable 對象,這個 Runnable 對象中有那個共享數據,例如,買票系統就可以這麼做
 
 
package com.hy.多線程高併發;

/**
* @Description:   多個線程行爲一致共同操作一個數據
 * 如果每個線程執行的代碼相同,可以使用同一個 Runnable 對象
 * 這個 Runnable 對象中有那個共享數據,例如
 * 買票系統就可以這麼做
* @Param:
* @return:
* @Author: Hy
* @Date: 2019
*/
public class Test5 {


    /**
     *共享數據類
     **/
    static class ShareData{
        private int num = 10 ;
        public synchronized void inc() {
            num++;
            System.out.println(Thread.currentThread().getName()+": invoke inc method num =" + num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    /**
     *多線程類
     **/
    static class RunnableCusToInc implements Runnable{

        private ShareData shareData;
        public RunnableCusToInc(ShareData data) {
            this.shareData = data;
        }
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                shareData.inc();
            }
        }
    }
    /**
     *測試方法
     **/
    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        for (int i = 0; i < 4; i++) {
            new Thread(new RunnableCusToInc(shareData),"Thread "+ i).start();
        }
    }
}




 
 

2. 多個線程行爲不一致共同操作一個數據

 
如果每個線程執行的代碼不同,這時候需要用不同的 Runnable 對象,有如下兩種方式來實現這些 Runnable 對象之間的數據共享:

 

1) 將共享數據封裝在另外一個對象中,然後將這個對象逐一傳遞給各個 Runnable 對象。每個線程對共享數據的操作方法也分配到那個對象身上去完成,這樣容易實現針對該數據進行的各個操作的互斥和通信。

 
 
package com.hy.多線程高併發;

/** 
* @Description:
 * 2. 多個線程行爲不一致共同操作一個數據
 * 如果每個線程執行的代碼不同,這時候需要用不同的 Runnable 對象,有如下兩種方式來實現這些 Runnable 對
 * 象之間的數據共享:
 * 1) 將共享數據封裝在另外一個對象中,然後將這個對象逐一傳遞給各個 Runnable 對象。每個線程對共享
 * 數據的操作方法也分配到那個對象身上去完成,這樣容易實現針對該數據進行的各個操作的互斥和通信。
* @Param:  
* @return:  
* @Author: Hy
* @Date: 2019
*/ 
public class Test6 {

    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        for (int i = 0; i < 4; i++) {
            if(i%2 == 0){
                new Thread(new RunnableCusToInc(shareData),"Thread "+ i).start();
            }else{
                new Thread(new RunnableCusToDec(shareData),"Thread "+ i).start();
            }
        }
    }

    //封裝共享數據類
    static class RunnableCusToInc implements Runnable{
        //封裝共享數據
        private ShareData shareData;
        public RunnableCusToInc(ShareData data) {
            this.shareData = data;
        }
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                shareData.inc();
            }
        }
    }

    //封裝共享數據類
    static class RunnableCusToDec implements Runnable{
        //封裝共享數據
        private ShareData shareData;
        public RunnableCusToDec(ShareData data) {
            this.shareData = data;
        }
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                shareData.dec();
            }
        }
    }

    /**
     *共享數據類
     **/
    static class ShareData{
        private int num = 10 ;
        public synchronized void inc() {
            num++;
            System.out.println(Thread.currentThread().getName()+": invoke inc method num =" + num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public void dec() {
            System.out.println("desc方法......");
        }
    }
}

 

package com.hy.多線程高併發;

/** 
* @Description:
 * 2. 多個線程行爲不一致共同操作一個數據
 * 如果每個線程執行的代碼不同,這時候需要用不同的 Runnable 對象,有如下兩種方式來實現這些 Runnable 對
 * 象之間的數據共享:
 * 1) 將共享數據封裝在另外一個對象中,然後將這個對象逐一傳遞給各個 Runnable 對象。每個線程對共享
 * 數據的操作方法也分配到那個對象身上去完成,這樣容易實現針對該數據進行的各個操作的互斥和通信。
* @Param:  
* @return:  
* @Author: Hy
* @Date: 2019
*/ 
public class Test6 {

    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        for (int i = 0; i < 4; i++) {
            if(i%2 == 0){
                new Thread(new RunnableCusToInc(shareData),"Thread "+ i).start();
            }else{
                new Thread(new RunnableCusToDec(shareData),"Thread "+ i).start();
            }
        }
    }

    //封裝共享數據類
    static class RunnableCusToInc implements Runnable{
        //封裝共享數據
        private ShareData shareData;
        public RunnableCusToInc(ShareData data) {
            this.shareData = data;
        }
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                shareData.inc();
            }
        }
    }

    //封裝共享數據類
    static class RunnableCusToDec implements Runnable{
        //封裝共享數據
        private ShareData shareData;
        public RunnableCusToDec(ShareData data) {
            this.shareData = data;
        }
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                shareData.dec();
            }
        }
    }

    /**
     *共享數據類
     **/
    static class ShareData{
        private int num = 10 ;
        public synchronized void inc() {
            num++;
            System.out.println(Thread.currentThread().getName()+": invoke inc method num =" + num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public void dec() {
            System.out.println("desc方法......");
            num--;
            System.err.println(Thread.currentThread().getName()+": invoke dec method num =" + num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

 

 

2) 將這些 Runnable 對象作爲某一個類中的內部類

 

共享數據作爲這個外部類中的成員變量,每個線程對共享數據的操作方法也分配給外部類

 
 
以便實現對共享數據進行的各個操作的互斥和通信,作爲內部類的各個Runnable 對象調用外部類的這些方法
 
 
 
 
package com.hy.多線程高併發;


/**
* @Description:
 * 2) 將這些 Runnable 對象作爲某一個類中的內部類,共享數據作爲這個外部類中的成員變量,每個線程對
 * 共享數據的操作方法也分配給外部類,以便實現對共享數據進行的各個操作的互斥和通信,作爲內部類的各個
 * Runnable 對象調用外部類的這些方法
* @Param:
* @return:
* @Author: Hy
* @Date: 2019
*/

public class Test7 {

    public static void main(String[] args) {
//公共數據
        final ShareData shareData = new ShareData();
        for (int i = 0; i < 4; i++) {
            if(i%2 == 0){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 5; i++) {
                            shareData.inc();
                        } }
                },"Thread "+ i).start();
            }else{
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 5; i++) {
                            shareData.dec();
                        } }
                },"Thread "+ i).start();
            }
        }
    }

    //
    static class ShareData{
        private int num = 10 ;

        //遞增
        public synchronized void inc() {
            num++;
            System.out.println(Thread.currentThread().getName()+": invoke inc method num =" + num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //遞減
        public synchronized void dec() {
            num--;
            System.err.println(Thread.currentThread().getName()+": invoke dec method num =" + num);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

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