線程基礎:線程(3)——JAVA中的基本線程操作(中)

來源:互聯網
上載者:User

線程基礎:線程(3)——JAVA中的基本線程操作(中)
1-4、注意synchronized關鍵字的使用

在前面的文章中我們主要講解的是線程中“對象鎖”的工作原理和操作方式。在講解synchronized關鍵字的時候,我們還提到了synchronized關鍵字可以標註的位置。大家經常看到相當部分的網貼,在它們的程式碼範例中將synchronized關鍵字載入到代碼的方法體上,然後告訴讀者:這個操作是安全執行緒的。代碼可能如下:

/** * 這個類的class對象進行檢查。 */public static synchronized void doSomething() {}/** * 對這個類的執行個體化對象進行檢查 */public synchronized void doOtherthing() {}

但事實上,一個對象是否是安全執行緒的除了添加synchronized關鍵字以外,更重要的還要看如何進行這個對象的操作。如下代碼中,我們展示了在兩個線程的doOtherthing方法(所謂的安全執行緒方法),去操作一個對象NOWVALUE:

package test.thread.yield;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.log4j.BasicConfigurator;/** * 用來在啟動後,等待喚醒 * @author yinwenjie */public class SyncThread implements Runnable {    /**     * 日誌     */    private static final Log LOGGER = LogFactory.getLog(SyncThread.class);    private Integer value;    private static Integer NOWVALUE;    static {        BasicConfigurator.configure();    }    public SyncThread(int value) {        this.value = value;    }    /**     * 對這個類的執行個體化對象進行檢查     */    private synchronized void doOtherthing() {        NOWVALUE = this.value;        LOGGER.info("當前NOWVALUE的值:" + NOWVALUE);    }    @Override    public void run() {        Thread currentThread = Thread.currentThread();        Long id = currentThread.getId();        this.doOtherthing();    }    public static void main(String[] args) throws Exception {        Thread syncThread1 = new Thread(new SyncThread(10));        Thread syncThread2 = new Thread(new SyncThread(100));        syncThread1.start();        syncThread2.start();    }}

從Debug的情況來看,可能出現靜態對象NOWVALUE的值出現了髒讀的情況:

0 [Thread-1] INFO test.thread.yield.SyncThread  - 當前NOWVALUE的值:100730 [Thread-0] INFO test.thread.yield.SyncThread  - 當前NOWVALUE的值:100

以下是代碼出現bug的原因:

syncThread1對象和syncThread2對象是SyncThread類的兩個不同執行個體。“private synchronized void doOtherthing()”方法中的synchronized關鍵字實際上進行同步檢查目標是不一樣的。

如果您要進行類的多個執行個體對象進行同步檢查,那麼應該對這個類的class對象進行同步檢查。寫法應該是:“private synchronized static void doOtherthing()”

當然為了對這個類(SyncThread)的class對象進行同步檢查,您甚至無需在靜態方法上標註synchronized關鍵字,而單獨標註SyncThread的class的對象鎖狀態檢查:

private void doOtherthing() {    synchronized (SyncThread.class) {        NOWVALUE = this.value;        LOGGER.info("當前NOWVALUE的值:" + NOWVALUE);    }}

所以,一個對象是否是安全執行緒的除了添加synchronized關鍵字以外,更重要的還要看如何進行這個對象的操作;標註了synchronized關鍵字的方法中,針對某個對象的操作不一定是安全執行緒的!

2、JAVA中的基本線程操作

這是前文中已經給出的線程狀態切換圖例,可能有的讀者還不能完全理解其中的切換條件,沒關係從本章節開始我們將詳細介紹JAVA中如何進行這些線程狀態的操作。<喎?http://www.bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPrP9wcvJz9K71cK92tTavbK94iZsZHF1bzu21M/zy/gmcmRxdW87tcTKsbry0tG+rczhtb21xHdhaXShondhaXQodGltZSmy2df30tTN4qOssb7Vwr3avau9sr3ibm90aWZ5oaJub3RpZnlBbGyhomludGVycnVwdKGiam9pbrrNc2xlZXC1yLLZ1/ehozwvcD4NCjxoMiBpZD0="2-1notify和notifyall操作">2-1、notify和notifyAll操作

在JAVA JDK中,對於notify方法和notifyAll方法的解釋分別是:

notify:

Wakes up a single thread that is waiting on this object’s monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread waits on an object’s monitor by calling one of the wait methods.

The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object.

notifyAll:

Wakes up all threads that are waiting on this object’s monitor. A thread waits on an object’s monitor by calling one of the wait methods.

The awakened threads will not be able to proceed until the current thread relinquishes the lock on this object. The awakened threads will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened threads enjoy no reliable privilege or disadvantage in being the next thread to lock this object.

為了說明notify方法和notifyAll方法的工作現象,下面我會為這兩個方法分別給出一段代碼,並進行詳細解釋。

2-1-1、notify方法的工作情況ParentNotifyThread類:
package test.thread.notify;import org.apache.log4j.BasicConfigurator;/** * 這個線程用來發出notify請求 * @author yinwenjie */public class ParentNotifyThread implements Runnable {    /**     * 這個對象的“鑰匙”,為每個ChildNotifyThread對象所持有,     * 類比這個對象為所有ChildNotifyThread對象都要進行獨佔的現象     */    public static final Object WAIT_CHILEOBJECT = new Object();    static {        BasicConfigurator.configure();    }    public static void main(String[] args) throws Exception {        new Thread(new ParentNotifyThread()).start();    }    public void run() {        /*         * 3個進行WAIT_CHILEOBJECT對象獨立搶佔的線程,觀察情況         * */        int maxIndex = 3;        for(int index = 0 ; index < maxIndex ; index++) {            ChildNotifyThread childNotify = new ChildNotifyThread();            Thread childNotifyThread = new Thread(childNotify);            childNotifyThread.start();        }        /*         * 請在這裡加eclipse斷點,         * 以便保證ChildNotifyThread中的wait()方法首先被執行了。         *          * 真實環境下,您可以通過一個布爾型(或者其他方式)進行阻塞判斷         * 還可以使用CountDownLatch類         * */        synchronized (ParentNotifyThread.WAIT_CHILEOBJECT) {            ParentNotifyThread.WAIT_CHILEOBJECT.notify();        }        // 沒有具體的示範含義;        // 只是為了保證ParentNotifyThread不會退出        synchronized (ParentNotifyThread.class) {            try {                ParentNotifyThread.class.wait();            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}
ChildNotifyThread類:
package test.thread.notify;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;/** * 用來在啟動後,等待喚醒 * @author yinwenjie */public class ChildNotifyThread implements Runnable {    /**     * 日誌     */    private static final Log LOGGER = LogFactory.getLog(ChildNotifyThread.class);    @Override    public void run() {        Thread currentThread = Thread.currentThread();        long id = currentThread.getId();        ChildNotifyThread.LOGGER.info("線程" + id + "啟動成功,準備進入等待狀態");        synchronized (ParentNotifyThread.WAIT_CHILEOBJECT) {            try {                ParentNotifyThread.WAIT_CHILEOBJECT.wait();            } catch (InterruptedException e) {                ChildNotifyThread.LOGGER.error(e.getMessage() , e);            }        }        //執行到這裡,說明線程被喚醒了        ChildNotifyThread.LOGGER.info("線程" + id + "被喚醒!");    }}

以上兩段代碼中,ParentNotifyThread類負責建立三個ChildNotifyThread類的對象,每一個ChildNotifyThread類的執行個體對象都持有ParentNotifyThread.WAIT_CHILEOBJECT對象的“鑰匙”,並通過wait方法退出ParentNotifyThread.WAIT_CHILEOBJECT對象的獨佔狀態(但是不歸還鎖),如所示:

然後我們通過ParentNotifyThread類中的ParentNotifyThread.WAIT_CHILEOBJECT.notify()方法解除阻塞狀態:

synchronized (ParentNotifyThread.WAIT_CHILEOBJECT) {    ParentNotifyThread.WAIT_CHILEOBJECT.notify();}

以上代碼的執行效果如下所示:

0 [Thread-1] INFO test.thread.notify.ChildNotifyThread  - 線程14啟動成功,準備進入等待狀態1 [Thread-2] INFO test.thread.notify.ChildNotifyThread  - 線程15啟動成功,準備進入等待狀態1 [Thread-3] INFO test.thread.notify.ChildNotifyThread  - 線程16啟動成功,準備進入等待狀態87285 [Thread-1] INFO test.thread.notify.ChildNotifyThread  - 線程14被喚醒!

實際上,我們只知道有三個ChildNotifyThread類的執行個體對象處於等待ParentNotifyThread.WAIT_CHILEOBJECT對象的“鎖芯”空閑;我們並不知道ParentNotifyThread.WAIT_CHILEOBJECT.notify()方法會將ParentNotifyThread.WAIT_CHILEOBJECT對象的“鎖芯”(獨佔權)交給這三個線程的哪一個線程(這個決定過程是由作業系統完成的)。而且我們還知道,ParentNotifyThread.WAIT_CHILEOBJECT.notify()方法只會喚醒等待ParentNotifyThread.WAIT_CHILEOBJECT對象“鎖芯”(獨佔權)的三個ChildNotifyThread類的執行個體對象中的一個

2-1-2、notifyAll方法的工作情況

實際上理解了notify()方法的工作情況,就不難理解notifyAll()方法的工作情況了。接下來,同樣是以上小節的代碼,我們將ParentNotifyThread類中的ParentNotifyThread.WAIT_CHILEOBJECT.notify()方法,替換成ParentNotifyThread.WAIT_CHILEOBJECT.notifyAll()方法。如下程式碼片段所示:

synchronized (ParentNotifyThread.WAIT_CHILEOBJECT) {    ParentNotifyThread.WAIT_CHILEOBJECT.notifyAll();}

然後我們觀察代碼的執行結果:

0 [Thread-2] INFO test.thread.notify.ChildNotifyThread  - 線程15啟動成功,準備進入等待狀態0 [Thread-1] INFO test.thread.notify.ChildNotifyThread  - 線程14啟動成功,準備進入等待狀態0 [Thread-3] INFO test.thread.notify.ChildNotifyThread  - 線程16啟動成功,準備進入等待狀態26834 [Thread-3] INFO test.thread.notify.ChildNotifyThread  - 線程16被喚醒!30108 [Thread-1] INFO test.thread.notify.ChildNotifyThread  - 線程14被喚醒!35368 [Thread-2] INFO test.thread.notify.ChildNotifyThread  - 線程15被喚醒!

我們看到這樣一個事實:在系統中等待arentNotifyThread.WAIT_CHILEOBJECT對象鎖的“鎖芯”(獨佔權)的三個線程被依次喚醒(依次得到獨佔權)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.