Java多線程基礎-使用多線程(三)


|-目錄
|??線程間通訊
??-|wait與notify方式
??-|等待(join)方式
??-|管道(pipeStream)方式
|??-線程共享變量【ThreadLocal】


-線程間通訊

?1.線程間通訊-wait與notify方式
??線程間通訊:調(diào)用wait方法前困檩,首先獲得wait對象的鎖。執(zhí)行wait方法后码泞,當前線程釋放鎖逛薇,并且狀態(tài)變更為Waiting狀態(tài),當前任務加入至“預處理隊列”中外里;調(diào)用notify方法前怎爵,首先獲得notify對象的鎖。執(zhí)行完notify synchronized同步代碼塊后盅蝗,當前線程釋放鎖鳖链,如果該線程沒有任務則該線程狀態(tài)變更為Terminated狀態(tài)。

圖1-1 線程通訊

public class ThreadWaitNotifyStateDemo {
    public static void main(String[] args) throws InterruptedException{
        final ServiceWaitNotify service = new ServiceWaitNotify();
        Thread thread_1 = new Thread("thread_1") {
            public void run() { 
                try {
                    service.testMethodA(); 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        };
        Thread thread_2 = new Thread("thread_2") {
            public void run() {
                try {
                service.testMethodB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        };
        thread_1.start();
        thread_2.start();
        Thread.sleep(1100);
        System.out.println(thread_1.getName() + ",state:" + thread_1.getState());
        Thread.sleep(500);
        System.out.println(thread_1.getName() + ",state:" + thread_1.getState());
    }
}

class ServiceWaitNotify {
    
    public void testMethodA() throws InterruptedException {
        Thread.sleep(1000);
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + ",進入Wait!" + Thread.currentThread().getState());
            this.wait();
            System.out.println(Thread.currentThread().getName() + ",退出Wait!" + Thread.currentThread().getState());
        }
        
    }
    
    public void testMethodB() throws InterruptedException {
        Thread.sleep(1500);
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + ",進入notify!" + Thread.currentThread().getState());
            this.notify();
            System.out.println(Thread.currentThread().getName() + ",退出notify!" + Thread.currentThread().getState());
        }
    }
}
圖1-2 線程通訊

??以上代碼:介紹了wait與notify的簡單使用墩莫,使用這兩個方法時必須有同種鎖的同步代碼塊芙委。同時說明了線程執(zhí)行相關方法時,線程所處狀態(tài)狂秦。如【圖1-2】可以看出當線程執(zhí)行wait方法后所處狀態(tài)為WATING,當notify線程同步代碼塊執(zhí)行完后灌侣,wait線程進入RUNNABLE狀態(tài),等待wait線程任務執(zhí)行完后進入TERMINATED狀態(tài)故痊。
?2.線程間通訊-等待(join)方式
??一個線程通過等待的方式顶瞳,獲取另一個線程數(shù)據(jù)執(zhí)行的結果。

public class ThreadJoinDemo {
    public static void main(String[] args) {
        try {
            JoinThread thread = new JoinThread("thread_1");
            thread.start();
            thread.join();
            System.out.println("" + Thread.currentThread().getName() + ",獲得["
                    + thread.getName() + "],value:" + thread.value);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class JoinThread extends Thread {
    public int value;
    
    public JoinThread (String name) {
        super(name);
    }
    
    @Override
    public void run() {
        System.out.println("" + Thread.currentThread().getName() + ",開始運行!");
        try {
            Thread.sleep(1000);
            value = 5;
        } catch(InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("" + Thread.currentThread().getName() + ",結束運行!");
    }
}

圖1-3 線程通訊

??從【圖1-3】可以看出愕秫,join方法是等待子線程全部執(zhí)行完后慨菱,主線程才往下執(zhí)行,這樣主線程就能獲取子線程修改后的值戴甩。
??這里要產(chǎn)生一個懷疑點例获,join方法的柱塞方式是否與wait方法相似妇多?
能夠產(chǎn)生上述懷疑的有兩點:
?1)join方法會throws InterruptedException【InterruptedException說明:線程在活動前或活動中啼辣,處于等待或睡眠狀態(tài)時粥喜,該線程被異常中斷則報上述異常。】缴川,故該異常必定與sleep或wait方法有關聯(lián)茉稠。
?2)join方法等待屬于不確定的等待,故join底層不可能使用sleep把夸。
??驗證:

public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

?從上面源碼可以看出而线,join底層就是使用wait方法,并且wait方法使用的鎖是線程對象本身“蚶海【這樣可以驗證調(diào)用join方法是否與wait方法一樣會釋放當前鎖對象】

public class ThreadJoinDemo {
    public static void main(String[] args) throws InterruptedException{
        JoinThread thread = new JoinThread("thread_1");
        synchronized (thread) {
            System.out.println("" + Thread.currentThread().getName() + ",獲得thread鎖!");
            thread.start();
            Thread.sleep(1000);
            System.out.println("" + Thread.currentThread().getName() + ",調(diào)用thread.join()");
            thread.join();
            System.out.println("" + Thread.currentThread().getName() + ",結束!");
        }
    }
}

class JoinThread extends Thread {
    public int value;
    
    public JoinThread (String name) {
        super(name);
    }
    
    @Override
    public void run() {
        synchronized (this) {
            System.out.println("" + Thread.currentThread().getName() + ",開始運行!");
            value = 5;
        }
        System.out.println("" + Thread.currentThread().getName() + ",結束運行!");
    }
}

圖1-4 線程通訊

??以上代碼可以看出嘹狞,如果join不能釋放鎖,則JoinThread run方法獲取不了鎖也執(zhí)行不了方法體中程序誓竿。但是【圖1-4】完全推翻了上述‘如果假設’磅网,證明了join底層就是wait,調(diào)用join方法與wait方法一樣會釋放當前鎖筷屡。
?3.線程間通訊-管道(pipeStream)方式
??兩個線程不借助文本涧偷,使用管道【輸出管道-寫數(shù)據(jù),輸入管道-讀數(shù)據(jù)】方式直接進行數(shù)據(jù)傳輸速蕊。
?管道通訊兩種方式:
?1)PipedInputStreamPipedOutputStream
?2)PipedReaderPipedWriter
?管道通訊使用注意點:
?1)管道通訊必須建立連接嫂丙。src.connect(snk)/snk.connect(src)【src:未連接的管道輸出流,snk:未鏈接的管道輸入流】规哲。
?2)PipedInputStream和PipedOutputStream不能在同一個線程使用》瘫恚【會出現(xiàn)死鎖現(xiàn)象】

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;


public class ThreadPipeStreamDemo {
    public static void main(String[] args) throws IOException {
        PipedInputStream in = new PipedInputStream();
        PipedOutputStream out = new PipedOutputStream();
        out.connect(in);
        ThreadWrite write = new ThreadWrite("write_thread", out);
        ThreadRead read = new ThreadRead("read_thread", in);
        write.start();
        read.start();
    }
}

class ThreadWrite extends Thread {
    private PipedOutputStream outputStream;
    public ThreadWrite (String name, PipedOutputStream out) {
        super(name);
        this.outputStream = out;
    }
    
    @Override
    public void run() {
        if (null == outputStream) return;
        try {
            System.out.println("" + Thread.currentThread().getName() + ",開始寫入數(shù)據(jù)!");
            for(int i = 0; i < 10; i++) {
                String writeData = "" + (i + 1);
                outputStream.write(writeData.getBytes(), 0 ,writeData.getBytes().length);
            }
            outputStream.flush();
            outputStream.close();
            System.out.println("" + Thread.currentThread().getName() + ",完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class ThreadRead extends Thread {
    private PipedInputStream inputStream;
    
    public ThreadRead (String name, PipedInputStream in) {
        super(name);
        this.inputStream = in;
    }
    
    @Override
    public void run() {
        if (null == inputStream) return;
        byte[] buffer = new byte[1048];
        System.out.println("" + Thread.currentThread().getName() + ",開始讀數(shù)據(jù)!");
        String data = "";
        try {
            int length = inputStream.read(buffer, 0, buffer.length);
            while (length != -1) {
                String str = new String(buffer, 0, length);
                data += str;
                length = inputStream.read(buffer, 0, buffer.length);
            }
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("" + Thread.currentThread().getName() + ",數(shù)據(jù):" + data);
    }
}
圖1-5 線程通訊

-線程共享變量【ThreadLocal】

??ThreadLocal類提供了線程局部(thread-local)變量唉锌,ThreadLocal與普通變量的區(qū)別在于:1,每個線程持有的是變量初始化副本的弱引用【直譯:每個線程都有自己的局部變量】竿奏。2袄简,ThreadLocal與線程生命周期相關聯(lián)【線程消失之后,其線程局部實例的所有副本都會被垃圾回收】泛啸。

public class ThreadLocalDemo {
    public static void main(String[] args) {
        final Tools tools = new Tools();
        Thread thread_1 = new Thread(new Runnable() {
            public void run() {
                int num = tools.getUid();
                System.out.println("" + Thread.currentThread().getName() + ",num:" + num);
            }
        }, "thread_1");
        Thread thread_2 = new Thread(new Runnable() {
            public void run() {
                tools.setUid(10);
                int num = tools.getUid();
                System.out.println("" + Thread.currentThread().getName() + ",num:" + num);
            }
        }, "thread_2");
        thread_1.start();
        thread_2.start();
    }
}
class Tools {
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
        protected Integer initialValue() {//ThreadLocal 獲取初始默認值
            return 0;
        };
    };
    
    public int getUid() {
        return threadLocal.get().intValue();  //ThreadLocal get()方法獲取當前變量值
    }
    
    public void setUid(int num) {
        threadLocal.set(num);  //ThreadLocal set()方法設置當前變量值
    }
}
圖1-5 線程共享變量

??從結果可以看出绿语,同一個ThreadLocal靜態(tài)變量但是獲取出來的值不同,這就解釋了上面局部變量的定義候址。

-總結

??這里主要講解線程間通訊吕粹,以及線程共享變量簡單使用。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末岗仑,一起剝皮案震驚了整個濱河市匹耕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荠雕,老刑警劉巖稳其,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異炸卑,居然都是意外死亡既鞠,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門盖文,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嘱蛋,“玉大人,你說我怎么就攤上這事』氩郏” “怎么了蒋失?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長桐玻。 經(jīng)常有香客問我篙挽,道長,這世上最難降的妖魔是什么镊靴? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任铣卡,我火速辦了婚禮,結果婚禮上偏竟,老公的妹妹穿的比我還像新娘煮落。我一直安慰自己,他們只是感情好踊谋,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布蝉仇。 她就那樣靜靜地躺著,像睡著了一般殖蚕。 火紅的嫁衣襯著肌膚如雪轿衔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天睦疫,我揣著相機與錄音害驹,去河邊找鬼。 笑死蛤育,一個胖子當著我的面吹牛宛官,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瓦糕,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼底洗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了刻坊?” 一聲冷哼從身側(cè)響起枷恕,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谭胚,沒想到半個月后徐块,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡灾而,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年胡控,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旁趟。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡昼激,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情橙困,我是刑警寧澤瞧掺,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站凡傅,受9級特大地震影響辟狈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜夏跷,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一哼转、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧槽华,春花似錦、人聲如沸猫态。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至终息,卻和暖如春贞让,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背续镇。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工摸航, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舅桩,地道東北人擂涛。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像恢暖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子杰捂,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355