成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java 多線程下篇 線程通訊

ethernet / 963人閱讀

摘要:等待通知機(jī)制利用,實(shí)現(xiàn)的一個(gè)生產(chǎn)者一個(gè)消費(fèi)者和一個(gè)單位的緩存的簡(jiǎn)單模型上面例子中我們生產(chǎn)了一個(gè)數(shù)據(jù)后就需要對(duì)這個(gè)數(shù)據(jù)進(jìn)行消費(fèi)如果生產(chǎn)了但數(shù)據(jù)沒有被獲取則生產(chǎn)線程會(huì)在等待中直到調(diào)用了方法后才會(huì)被繼續(xù)執(zhí)行反之也是一樣的也就是說(shuō)方法是使線程暫停

等待/通知機(jī)制

利用wait,notify實(shí)現(xiàn)的一個(gè)生產(chǎn)者、一個(gè)消費(fèi)者和一個(gè)單位的緩存的簡(jiǎn)單模型:

public class QueueBuffer {
    int n;
    boolean valueSet = false;

    synchronized int get() {
        if (!valueSet)
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println("InterruptedException caught");
            }
        System.out.println("Got: " + n);
        valueSet = false;
        notify();
        return n;
    }

    synchronized void put(int n) {
        if (valueSet)
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println("InterruptedException caught");
            }
        this.n = n;
        valueSet = true;
        System.out.println("Put: " + n);
        notify();
    }
}
public class Producer implements Runnable {
    
    private QueueBuffer q;

    Producer(QueueBuffer q) {
        this.q = q;
        new Thread(this, "Producer").start();
    }

    public void run() {
        int i = 0;
        while (true) {
            q.put(i++);
        }
    }

}
public class Consumer implements Runnable {
    
    private QueueBuffer q;

    Consumer(QueueBuffer q) {
        this.q = q;
        new Thread(this, "Consumer").start();
    }

    public void run() {
        while (true) {
            q.get();
        }
    }

}
public class Main {

    public static void main(String[] args) {
        QueueBuffer q = new QueueBuffer(); 
        new Producer(q); 
        new Consumer(q); 
        System.out.println("Press Control-C to stop."); 
    }

}

上面例子中, 我們生產(chǎn)了一個(gè)數(shù)據(jù)后就需要對(duì)這個(gè)數(shù)據(jù)進(jìn)行消費(fèi). 如果生產(chǎn)了但數(shù)據(jù)沒有被獲取, 則生產(chǎn)線程會(huì)在等待中. 直到調(diào)用了 notify() 方法后才會(huì)被繼續(xù)執(zhí)行. 反之也是一樣的.

也就是說(shuō), wait() 方法是使線程暫停; notify() 方法是使線程繼續(xù)運(yùn)行.

但是在使用時(shí)需要注意:

1.執(zhí)行wait, notify時(shí),不獲得鎖會(huì)如何?

public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();
        obj.wait();
        obj.notifyAll();
}

執(zhí)行以上代碼, 會(huì)拋出java.lang.IllegalMonitorStateException的異常.

2.執(zhí)行wait, notify時(shí), 不獲得該對(duì)象的鎖會(huì)如何?

public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();
        Object lock = new Object();
        synchronized (lock) {
            obj.wait();
            obj.notifyAll();
        }
    }

執(zhí)行代碼,同樣會(huì)拋出java.lang.IllegalMonitorStateException的異常

該對(duì)象的鎖 指的就是 obj 對(duì)象的鎖.

3.為什么在執(zhí)行 wait, notify時(shí), 必須獲得該對(duì)象的鎖?

我們需要先知道 synchronized 的作用:

Java中每一個(gè)對(duì)象都可以成為一個(gè)監(jiān)視器(Monitor), 該Monitor由一個(gè)鎖(lock), 一個(gè)等待隊(duì)列(waiting queue), 一個(gè)入口隊(duì)列(entry queue).

對(duì)于一個(gè)對(duì)象的方法, 如果沒有 synchronized 關(guān)鍵字, 該方法可以被任意數(shù)量的線程, 在任意時(shí)刻調(diào)用.

對(duì)于添加了 synchronized 關(guān)鍵字的方法, 任意時(shí)刻只能被唯一的一個(gè)獲得了對(duì)象實(shí)例鎖的線程調(diào)用.

synchronized 用于實(shí)現(xiàn)多線程的同步操作.

當(dāng)一個(gè)線程在執(zhí)行 synchronized 的方法內(nèi)部, 調(diào)用了 wait() 后, 該線程會(huì)釋放該對(duì)象的鎖, 然后該線程會(huì)被添加到該對(duì)象的等待隊(duì)列中(waiting queue), 只要該線程在等待隊(duì)列中, 就會(huì)一直處于閑置狀態(tài), 不會(huì)被調(diào)度執(zhí)行.

要注意 wait() 方法會(huì)強(qiáng)迫線程先進(jìn)行釋放鎖操作, 所以在調(diào)用 wait() 時(shí), 該線程必須已經(jīng)獲得鎖, 否則會(huì)拋出異常(IllegalMonitorStateException). 由于 wait()synchonized 的方法內(nèi)部被執(zhí)行, 鎖一定已經(jīng)獲得, 就不會(huì)拋出異常了.

當(dāng)一個(gè)線程調(diào)用一個(gè)對(duì)象的 notify() 方法時(shí), 調(diào)度器會(huì)從所有處于該對(duì)象等待隊(duì)列 (waiting queue) 的線程中取出任意一個(gè)線程, 將其添加到入口隊(duì)列 (entry queue) 中. 然后在入口隊(duì)列中的多個(gè)線程就會(huì)競(jìng)爭(zhēng)對(duì)象的鎖, 得到鎖的線程就可以繼續(xù)執(zhí)行. 如果等待隊(duì)列中(waiting queue)沒有線程, notify() 方法不會(huì)產(chǎn)生任何作用.

線程狀態(tài)

NEW: 線程實(shí)例化時(shí)的默認(rèn)狀態(tài).

RUNNABLE: 一旦線程開始執(zhí)行, 它就會(huì)移動(dòng)到Runnable狀態(tài). 請(qǐng)注意, 等待獲取 CPU 以供執(zhí)行的線程仍處于此狀態(tài).

BLOCKED: 線程一旦被阻塞, 就會(huì)等待監(jiān)視器鎖, 并且移動(dòng)到阻塞狀態(tài). 有兩種方式可以進(jìn)入阻塞狀態(tài).

synchronised 同步代碼塊或同步方法.

調(diào)用 Object.Wait 方法.

WAITING: 調(diào)用下列方法來(lái)將線程變?yōu)榈却隣顟B(tài)

Object.wait without a timeout

Thread.join without a timeout

LockSupport.park

TIMED_WAITING: 調(diào)用下列方法將線程變?yōu)槌瑫r(shí)等待

Thread.sleep

Object.wait with a timeout

Thread.join with a timeout

LockSupport.parkNanos

LockSupport.parkUntil

TERMINATED: 一旦線程終止, 它就會(huì)移動(dòng)到這種狀態(tài).

通過(guò)管道進(jìn)行線程通信: 字節(jié)流

用來(lái)讀取管道中的數(shù)據(jù)

public class ReadData extends Thread {

    private PipedInputStream pipedInputStream;

    public ReadData(PipedInputStream pipedInputStream) {
        this.pipedInputStream = pipedInputStream;
    }

    @Override
    public void run() {
        try {
            System.out.println("read :");
            byte[] byteArray = new byte[20];
            int readLen = this.pipedInputStream.read(byteArray);
            String newData = "";
            while(readLen != -1) {
                newData += new String(byteArray, 0, readLen);
                readLen = this.pipedInputStream.read(byteArray);

            }
            
            System.out.println(newData);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

用來(lái)給管道發(fā)送數(shù)據(jù)

public class WriteData extends Thread {

    private PipedOutputStream pipedOutputStream;

    public WriteData(PipedOutputStream pipedOutputStream) {
        this.pipedOutputStream = pipedOutputStream;
    }

    @Override
    public void run() {
        try {
            System.out.println("write :");
            for (int i = 0; i < 300; i++) {
                String outData = "" + (i + 1);
                this.pipedOutputStream.write(outData.getBytes());
                System.out.print(outData);
            }
            System.out.println();
            this.pipedOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
    public static void main(String[] args) throws IOException {

        PipedInputStream pipedInputStream = new PipedInputStream();
        PipedOutputStream pipedOutputStream = new PipedOutputStream();

        pipedOutputStream.connect(pipedInputStream);

        WriteData writeData = new WriteData(pipedOutputStream);
        ReadData readData = new ReadData(pipedInputStream);

        writeData.start();
        readData.start();

    }

pipedOutputStream.connect(pipedInputStream); 用來(lái)將兩個(gè)流之間產(chǎn)生通訊.

對(duì)于字節(jié)流和字符流是一樣的, 只需要使用 PipedWriterPipedReader.
join 方法使用

在一個(gè)線程(父線程)中創(chuàng)建另一個(gè)線程(子線程), 有些情況下, 我們需要等待子線程執(zhí)行完成后, 父線程在繼續(xù)執(zhí)行.

比如子線程處理一個(gè)數(shù)據(jù), 父線程要取得這個(gè)數(shù)據(jù)中的值, 可以考慮使用 join 方法來(lái)實(shí)現(xiàn).

不使用 join 方法前的問題
public class MyThread extends Thread {
    @Override
    public void run() {
        int i = (int) (Math.random() * 10000);
        System.out.println(i);
        try {
            Thread.sleep(i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
    public static void main(String[] args) throws IOException {

        MyThread myThread = new MyThread();
        myThread.start();

        //Thread.sleep(?);

        System.out.println("我想當(dāng) myThread 執(zhí)行完畢后再執(zhí)行");
        System.out.println("但上面代碼中 sleep 中的值應(yīng)該寫多少?");
        System.out.println("答案是: 值不能確定 :) ");
    }
使用 join 方法來(lái)解決問題
    public static void main(String[] args) throws IOException, InterruptedException {

        MyThread myThread = new MyThread();
        myThread.start();
        myThread.join();

        System.out.println("我想當(dāng) myThread 對(duì)象執(zhí)行完畢后我再執(zhí)行, 我做到了");
    }
join 與 synchronized 的區(qū)別是: join 在內(nèi)部使用 wait 方法進(jìn)行等待, 而 synchronized 關(guān)鍵字使用的是 "對(duì)象監(jiān)視器" 原理做完同步.
并且如果遇到 interrupt 方法則會(huì)拋出, InterruptedException
join(long) 方法的使用

方法 join(long) 中的參數(shù)是設(shè)置等待的時(shí)間.

public class MyThread extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(5000);
            System.out.println("執(zhí)行完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public static void main(String[] args) throws IOException, InterruptedException {

    MyThread myThread = new MyThread();
    myThread.start();
    myThread.join(2000);

    System.out.println("等待2秒后執(zhí)行");
}

從打印結(jié)果來(lái)看主線程只等待了兩秒后輸出了 "等待2秒后執(zhí)行", 和 sleep 執(zhí)行結(jié)果是一樣的. 主要原因還是來(lái)自于這2個(gè)方法同步的處理上.

方法 join(long) 與 sleep(long) 的區(qū)別, join(long) 會(huì)釋放鎖, sleep(long) 不會(huì)釋放鎖.
ThreadLocal 類的使用

變量值的共享可以使用 public static 變量的形式, 所有的線程都使用同一個(gè) public static 變量. 如果想實(shí)現(xiàn)每一個(gè)線程都有自己的共享變量可以使用 ThreadLocal 類.

類 ThreadLocal 主要解決的就是每個(gè)線程綁定自己的值, 可以比喻成全局存放數(shù)據(jù)的盒子, 盒子中可以存儲(chǔ)每個(gè)線程的私有數(shù)據(jù).

多個(gè)線程之間是隔離的.
public class Tools {
    public static ThreadLocal threadLocal = new ThreadLocal();
}
public class ThreadA extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 100; i++) {
                Tools.threadLocal.set("ThreadA" + (i + 1));
                System.out.println("ThreadA get Value=" + Tools.threadLocal.get());
                Thread.sleep(200);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class ThreadB extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 100; i++) {
                Tools.threadLocal.set("ThreadB" + (i + 1));
                System.out.println("ThreadB get Value=" + Tools.threadLocal.get());
                Thread.sleep(200);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
類 InheritableThreadLocal 的使用

使用 InheritableThreadLocal 類可以在子線程中取得父線程繼承下來(lái)的值.

值繼承
public class InheritableThreadLocalEx extends InheritableThreadLocal {
    @Override
    protected Object initialValue() {
        return new Date().getTime();
    }
}
public class Tools {
    public static InheritableThreadLocalEx inheritableThreadLocalEx = new InheritableThreadLocalEx();
}
public class ThreadA extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                System.out.println("ThreadA get Value=" + Tools.inheritableThreadLocalEx.get());
                Thread.sleep(100);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public static void main(String[] args) throws IOException, InterruptedException {

    for (int i = 0; i < 10; i++) {
        System.out.println("Main get Value=" + Tools.inheritableThreadLocalEx.get());
        Thread.sleep(100);
    }

    ThreadA threadA = new ThreadA();
    threadA.start();
}
值繼承再修改
public class InheritableThreadLocalEx extends InheritableThreadLocal {
    @Override
    protected Object initialValue() {
        return new Date().getTime();
    }

    @Override
    protected Object childValue(Object parentValue) {
        return parentValue + " 我在子線程加的~";
    }
}
注意, 如果子線程在取得值得同, 主線程將 InheritableThreadLocal 中的值進(jìn)行更改, 那么子線程取到的值還是就值.

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/73795.html

相關(guān)文章

  • Java貓說(shuō)】Java線程之內(nèi)存可見性(下篇

    摘要:閱讀本文約分鐘上一次我們說(shuō)到互斥代碼的實(shí)現(xiàn)過(guò)程,如果有忘記或不清楚的可以去上篇看看。貓說(shuō)多線程之內(nèi)存可見性上篇今天我們了解下重排序。 閱讀本文約3分鐘 上一次我們說(shuō)到synchronized互斥代碼的實(shí)現(xiàn)過(guò)程,如果有忘記或不清楚的可以去上篇看看?!綣ava貓說(shuō)】Java多線程之內(nèi)存可見性(上篇) 今天我們了解下重排序。 其使代碼書寫的順序與實(shí)現(xiàn)執(zhí)行的順序不同,指令重排序是編譯器或處理...

    elliott_hu 評(píng)論0 收藏0
  • Java貓說(shuō)】Java線程之內(nèi)存可見性(上篇)

    摘要:貓說(shuō)多線程之內(nèi)存可見性下篇?dú)g迎你留言討論屬于你的見解,畢竟每個(gè)人的味蕾都不一樣,這杯咖啡有吸引到你嗎好像又是一個(gè)槽糕的比喻本文已轉(zhuǎn)載個(gè)人技術(shù)公眾號(hào)歡迎留言討論與點(diǎn)贊上一篇推薦貓說(shuō)主數(shù)據(jù)類型和引用下一篇推薦貓說(shuō)多線程之內(nèi)存可見性下篇 閱讀本文約3分鐘 本文大致講述兩種線程實(shí)現(xiàn)的可見性,或許你已經(jīng)提前想到了,那說(shuō)明你的基礎(chǔ)很好,我們要聊聊synchronized實(shí)現(xiàn)可見性與volatil...

    khlbat 評(píng)論0 收藏0
  • Java并發(fā)編程之線程通訊(上)wait/notify機(jī)制

    摘要:用線程表示維修的過(guò)程維修結(jié)束把廁所置為可用狀態(tài)維修工把廁所修好了,準(zhǔn)備釋放鎖了這個(gè)維修計(jì)劃的內(nèi)容就是當(dāng)維修工進(jìn)入廁所之后,先把門鎖上,然后開始維修,維修結(jié)束之后把的字段設(shè)置為,以表示廁所可用。 線程間通信 如果一個(gè)線程從頭到尾執(zhí)行完也不和別的線程打交道的話,那就不會(huì)有各種安全性問題了。但是協(xié)作越來(lái)越成為社會(huì)發(fā)展的大勢(shì),一個(gè)大任務(wù)拆成若干個(gè)小任務(wù)之后,各個(gè)小任務(wù)之間可能也需要相互協(xié)作最終...

    Lionad-Morotar 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<