1. 程式人生 > >java高併發系列 - 第6天:執行緒的基本操作

java高併發系列 - 第6天:執行緒的基本操作

新建執行緒

新建執行緒很簡單。只需要使用new關鍵字建立一個執行緒物件,然後呼叫它的start()啟動執行緒即可。

Thread thread1 = new Thread1();
t1.start();

那麼執行緒start()之後,會幹什麼呢?執行緒有個run()方法,start()會建立一個新的執行緒並讓這個執行緒執行run()方法。

這裡需要注意,下面程式碼也能通過編譯,也能正常執行。但是,卻不能新建一個執行緒,而是在當前執行緒中呼叫run()方法,將run方法只是作為一個普通的方法呼叫。

Thread thread = new Thread1();
thread1.run();

所以,希望大家注意,呼叫start方法和直接呼叫run方法的區別。

start方法是啟動一個執行緒,run方法只會在當前執行緒中序列的執行run方法中的程式碼。

預設情況下, 執行緒的run方法什麼都沒有,啟動一個執行緒之後馬上就結束了,所以如果你需要執行緒做點什麼,需要把您的程式碼寫到run方法中,所以必須重寫run方法。

Thread thread1 = new Thread() {
            @Override
            public void run() {
                System.out.println("hello,我是一個執行緒!");
            }
        };
thread1.start();

上面是使用匿名內部類實現的,重寫了Thread的run方法,並且列印了一條資訊。我們可以通過繼承Thread類,然後重寫run方法,來自定義一個執行緒。但考慮java是單繼承的,從擴充套件性上來說,我們實現一個介面來自定義一個執行緒更好一些,java中剛好提供了Runnable介面來自定義一個執行緒。

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

Thread類有一個非常重要的構造方法:

public Thread(Runnable target)

我們在看一下Thread的run方法:

public void run() {
        if (target != null) {
            target.run();
        }
    }

當我們啟動執行緒的start方法之後,執行緒會執行run方法,run方法中會呼叫Thread構造方法傳入的target的run方法。

實現Runnable介面是比較常見的做法,也是推薦的做法。

終止執行緒

一般來說執行緒執行完畢就會結束,無需手動關閉。但是如果我們想關閉一個正在執行的執行緒,有什麼方法呢?可以看一下Thread類中提供了一個stop()方法,呼叫這個方法,就可以立即將一個執行緒終止,非常方便。

package com.itsoku.chat01;

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;

/**
 * <b>description</b>: <br>
 * <b>time</b>:2019/7/12 17:18 <br>
 * <b>author</b>:微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
 */
@Slf4j
public class Demo01 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread() {
            @Override
            public void run() {
                log.info("start");
                boolean flag = true;
                while (flag) {
                    ;
                }
                log.info("end");
            }
        };
        thread1.setName("thread1");
        thread1.start();
        //當前執行緒休眠1秒
        TimeUnit.SECONDS.sleep(1);
        //關閉執行緒thread1
        thread1.stop();
        //輸出執行緒thread1的狀態
        log.info("{}", thread1.getState());
        //當前執行緒休眠1秒
        TimeUnit.SECONDS.sleep(1);
        //輸出執行緒thread1的狀態
        log.info("{}", thread1.getState());
    }
}

執行程式碼,輸出:

18:02:15.312 [thread1] INFO com.itsoku.chat01.Demo01 - start
18:02:16.311 [main] INFO com.itsoku.chat01.Demo01 - RUNNABLE
18:02:17.313 [main] INFO com.itsoku.chat01.Demo01 - TERMINATED

程式碼中有個死迴圈,呼叫stop方法之後,執行緒thread1的狀態變為TERMINATED(結束狀態),執行緒停止了。

我們使用idea或者eclipse的時候,會發現這個方法是一個廢棄的方法,也就是說,在將來,jdk可能就會移除該方法。

stop方法為何會被廢棄而不推薦使用?stop方法過於暴力,強制把正在執行的方法停止了。

大家是否遇到過這樣的場景:電力系統需要維修,此時咱們正在寫程式碼,維修人員直接將電源關閉了,程式碼還沒儲存的,是不是很崩潰,這種方式就像直接呼叫執行緒的stop方法類似。執行緒正在執行過程中,被強制結束了,可能會導致一些意想不到的後果。可以給大家傳送一個通知,告訴大家儲存一下手頭的工作,將電腦關閉。

執行緒中斷

在java中,執行緒中斷是一種重要的執行緒寫作機制,從表面上理解,中斷就是讓目標執行緒停止執行的意思,實際上並非完全如此。在上面中,我們已經詳細討論了stop方法停止執行緒的壞處,jdk中提供了更好的中斷執行緒的方法。嚴格的說,執行緒中斷並不會使執行緒立即退出,而是給執行緒傳送一個通知,告知目標執行緒,有人希望你退出了!至於目標執行緒接收到通知之後如何處理,則完全由目標執行緒自己決定,這點很重要,如果中斷後,執行緒立即無條件退出,我們又會到stop方法的老問題。

Thread提供了3個與執行緒中斷有關的方法,這3個方法容易混淆,大家注意下:

public void interrupt() //中斷執行緒
public boolean isInterrupted() //判斷執行緒是否被中斷
public static boolean interrupted()  //判斷執行緒是否被中斷,並清除當前中斷狀態

interrupt()方法是一個例項方法,它通知目標執行緒中斷,也就是設定中斷標誌位為true,中斷標誌位表示當前執行緒已經被中斷了。isInterrupted()方法也是一個例項方法,它判斷當前執行緒是否被中斷(通過檢查中斷標誌位)。最後一個方法interrupted()是一個靜態方法,返回boolean型別,也是用來判斷當前執行緒是否被中斷,但是同時會清除當前執行緒的中斷標誌位的狀態。

while (true) {
            if (this.isInterrupted()) {
                System.out.println("我要退出了!");
                break;
            }
        }
    }
};
thread1.setName("thread1");
thread1.start();
TimeUnit.SECONDS.sleep(1);
thread1.interrupt();

上面程式碼中有個死迴圈,interrupt()方法被呼叫之後,執行緒的中斷標誌將被置為true,迴圈體中通過檢查執行緒的中斷標誌是否為ture(this.isInterrupted())來判斷執行緒是否需要退出了。

再看一種中斷的方法:

static volatile boolean isStop = false;

public static void main(String[] args) throws InterruptedException {
    Thread thread1 = new Thread() {
        @Override
        public void run() {
            while (true) {
                if (isStop) {
                    System.out.println("我要退出了!");
                    break;
                }
            }
        }
    };
    thread1.setName("thread1");
    thread1.start();
    TimeUnit.SECONDS.sleep(1);
    isStop = true;
}

程式碼中通過一個變數isStop來控制執行緒是否停止。

通過變數控制和執行緒自帶的interrupt方法來中斷執行緒有什麼區別呢?

如果一個執行緒呼叫了sleep方法,一直處於休眠狀態,通過變數控制,還可以中斷執行緒麼?大家可以思考一下。

此時只能使用執行緒提供的interrupt方法來中斷執行緒了。

public static void main(String[] args) throws InterruptedException {
    Thread thread1 = new Thread() {
        @Override
        public void run() {
            while (true) {
                //休眠100秒
                try {
                    TimeUnit.SECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我要退出了!");
                break;
            }
        }
    };
    thread1.setName("thread1");
    thread1.start();
    TimeUnit.SECONDS.sleep(1);
    thread1.interrupt();
}

呼叫interrupt()方法之後,執行緒的sleep方法將會丟擲InterruptedException異常。

Thread thread1 = new Thread() {
    @Override
    public void run() {
        while (true) {
            //休眠100秒
            try {
                TimeUnit.SECONDS.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (this.isInterrupted()) {
                System.out.println("我要退出了!");
                break;
            }
        }
    }
};

執行上面的程式碼,發現程式無法終止。為什麼?

程式碼需要改為:

Thread thread1 = new Thread() {
    @Override
    public void run() {
        while (true) {
            //休眠100秒
            try {
                TimeUnit.SECONDS.sleep(100);
            } catch (InterruptedException e) {
                this.interrupt();
                e.printStackTrace();
            }
            if (this.isInterrupted()) {
                System.out.println("我要退出了!");
                break;
            }
        }
    }
};

上面程式碼可以終止。

注意:sleep方法由於中斷而丟擲異常之後,執行緒的中斷標誌會被清除(置為false),所以在異常中需要執行this.interrupt()方法,將中斷標誌位置為true

等待(wait)和通知(notify)

為了支援多執行緒之間的協作,JDK提供了兩個非常重要的方法:等待wait()方法和通知notify()方法。這2個方法並不是在Thread類中的,而是在Object類中定義的。這意味著所有的物件都可以呼叫者兩個方法。

public final void wait() throws InterruptedException;
public final native void notify();

當在一個物件例項上呼叫wait()方法後,當前執行緒就會在這個物件上等待。這是什麼意思?比如線上程A中,呼叫了obj.wait()方法,那麼執行緒A就會停止繼續執行,轉為等待狀態。等待到什麼時候結束呢?執行緒A會一直等到其他執行緒呼叫obj.notify()方法為止,這時,obj物件成為了多個執行緒之間的有效通訊手段。

那麼wait()方法和notify()方法是如何工作的呢?如圖2.5展示了兩者的工作過程。如果一個執行緒呼叫了object.wait()方法,那麼它就會進出object物件的等待佇列。這個佇列中,可能會有多個執行緒,因為系統可能執行多個執行緒同時等待某一個物件。當object.notify()方法被呼叫時,它就會從這個佇列中隨機選擇一個執行緒,並將其喚醒。這裡希望大家注意一下,這個選擇是不公平的,並不是先等待執行緒就會優先被選擇,這個選擇完全是隨機的。

除notify()方法外,Object獨享還有一個nofiyAll()方法,它和notify()方法的功能類似,不同的是,它會喚醒在這個等待佇列中所有等待的執行緒,而不是隨機選擇一個。

這裡強調一點,Object.wait()方法並不能隨便呼叫。它必須包含在對應的synchronize語句彙總,無論是wait()方法或者notify()方法都需要首先獲取目標獨享的一個監視器。圖2.6顯示了wait()方法和nofiy()方法的工作流程細節。其中T1和T2表示兩個執行緒。T1在正確執行wait()方法錢,必須獲得object物件的監視器。而wait()方法在執行後,會釋放這個監視器。這樣做的目的是使其他等待在object物件上的執行緒不至於因為T1的休眠而全部無法正常執行。

執行緒T2在notify()方法呼叫前,也必須獲得object物件的監視器。所幸,此時T1已經釋放了這個監視器,因此,T2可以順利獲得object物件的監視器。接著,T2執行了notify()方法嘗試喚醒一個等待執行緒,這裡假設喚醒了T1。T1在被喚醒後,要做的第一件事並不是執行後續程式碼,而是要嘗試重新獲得object物件的監視器,而這個監視器也正是T1在wait()方法執行前所持有的那個。如果暫時無法獲得,則T1還必須等待這個監視器。當監視器順利獲得後,T1才可以在真正意義上繼續執行。

給大家上個例子:

package com.itsoku.chat01;

/**
 * <b>description</b>: <br>
 * <b>time</b>:2019/7/12 17:18 <br>
 * <b>author</b>:微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
 */
public class Demo06 {
    static Object object = new Object();

    public static class T1 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                System.out.println(System.currentTimeMillis() + ":T1 start!");
                try {
                    System.out.println(System.currentTimeMillis() + ":T1 wait for object");
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis() + ":T1 end!");
            }
        }
    }

    public static class T2 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                System.out.println(System.currentTimeMillis() + ":T2 start,notify one thread! ");
                object.notify();
                System.out.println(System.currentTimeMillis() + ":T2 end!");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new T1().start();
        new T2().start();
    }
}

執行結果:

1562934497212:T1 start!
1562934497212:T1 wait for object
1562934497212:T2 start,notify one thread!
1562934497212:T2 end!
1562934499213:T1 end!

注意下列印結果,T2呼叫notify方法之後,T1並不能立即繼續執行,而是要等待T2釋放objec投遞鎖之後,T1重新成功獲取鎖後,才能繼續執行。因此最後2行日誌相差了2秒(因為T2呼叫notify方法後休眠了2秒)。

注意:Object.wait()方法和Thread.sleeep()方法都可以讓現場等待若干時間。除wait()方法可以被喚醒外,另外一個主要的區別就是wait()方法會釋放目標物件的鎖,而Thread.sleep()方法不會釋放鎖。

再給大家講解一下wait(),notify(),notifyAll(),加深一下理解:

可以這麼理解,obj物件上有2個佇列,如圖1,q1:等待佇列,q2:準備獲取鎖的佇列;兩個佇列都為空。

obj.wait()過程:

synchronize(obj){
    obj.wait();
}

假如有3個執行緒,t1、t2、t3同時執行上面程式碼,t1、t2、t3會進入q2佇列,如圖2,進入q2的佇列的這些執行緒才有資格去爭搶obj的鎖,假設t1爭搶到了,那麼t2、t3機型在q2中等待著獲取鎖,t1進入程式碼塊執行wait()方法,此時t1會進入q1佇列,然後系統會通知q2佇列中的t2、t3去爭搶obj的鎖,搶到之後過程如t1的過程。最後t1、t2、t3都進入了q1佇列,如圖3。

上面過程之後,又來了執行緒t4執行了notify()方法,如下:**

synchronize(obj){
    obj.notify();
}

t4會獲取到obj的鎖,然後執行notify()方法,系統會從q1佇列中隨機取一個執行緒,將其加入到q2佇列,假如t2運氣比較好,被隨機到了,然後t2進入了q2佇列,如圖4,進入q2的佇列的鎖才有資格爭搶obj的鎖,t4執行緒執行完畢之後,會釋放obj的鎖,此時佇列q2中的t2會獲取到obj的鎖,然後繼續執行,執行完畢之後,q1中包含t1、t3,q2佇列為空,如圖5

接著又來了個t5佇列,執行了notifyAll()方法,如下:

synchronize(obj){
    obj.notifyAll();
}

2.呼叫obj.wait()方法,當前執行緒會加入佇列queue1,然後會釋放obj物件的鎖

t5會獲取到obj的鎖,然後執行notifyAll()方法,系統會將佇列q1中的執行緒都移到q2中,如圖6,t5執行緒執行完畢之後,會釋放obj的鎖,此時佇列q2中的t1、t3會爭搶obj的鎖,爭搶到的繼續執行,未增強到的帶鎖釋放之後,系統會通知q2中的執行緒繼續爭搶索,然後繼續執行,最後兩個佇列中都為空了。

掛起(suspend)和繼續執行(resume)執行緒

Thread類中還有2個方法,即執行緒掛起(suspend)和繼續執行(resume),這2個操作是一對相反的操作,被掛起的執行緒,必須要等到resume()方法操作後,才能繼續執行。系統中已經標註著2個方法過時了,不推薦使用。

系統不推薦使用suspend()方法去掛起執行緒是因為suspend()方法導致執行緒暫停的同時,並不會釋放任何鎖資源。此時,其他任何執行緒想要訪問被它佔用的鎖時,都會被牽連,導致無法正常執行(如圖2.7所示)。直到在對應的執行緒上進行了resume()方法操作,被掛起的執行緒才能繼續,從而其他所有阻塞在相關鎖上的執行緒也可以繼續執行。但是,如果resume()方法操作意外地在suspend()方法前就被執行了,那麼被掛起的執行緒可能很難有機會被繼續執行了。並且,更嚴重的是:它所佔用的鎖不會被釋放,因此可能會導致整個系統工作不正常。而且,對於被掛起的執行緒,從它執行緒的狀態上看,居然還是Runnable狀態,這也會影響我們隊系統當前狀態的判斷。

上個例子:

/**
 * <b>description</b>: <br>
 * <b>time</b>:2019/7/12 17:18 <br>
 * <b>author</b>:微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
 */
public class Demo07 {
    static Object object = new Object();

    public static class T1 extends Thread {
        public T1(String name) {
            super(name);
        }

        @Override
        public void run() {
            synchronized (object) {
                System.out.println("in " + this.getName());
                Thread.currentThread().suspend();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        T1 t1 = new T1("t1");
        t1.start();
        Thread.sleep(100);
        T1 t2 = new T1("t2");
        t2.start();
        t1.resume();
        t2.resume();
        t1.join();
        t2.join();
    }
}

執行程式碼輸出:

in t1
in t2

我們會發現程式不會結束,執行緒t2被掛起了,導致程式無法結束,使用jstack命令檢視執行緒堆疊資訊可以看到:

"t2" #13 prio=5 os_prio=0 tid=0x000000002796c000 nid=0xa3c runnable [0x000000002867f000]
   java.lang.Thread.State: RUNNABLE
        at java.lang.Thread.suspend0(Native Method)
        at java.lang.Thread.suspend(Thread.java:1029)
        at com.itsoku.chat01.Demo07$T1.run(Demo07.java:20)
        - locked <0x0000000717372fc0> (a java.lang.Object)

發現t2執行緒在suspend0處被掛起了,t2的狀態竟然還是RUNNABLE狀態,執行緒明明被掛起了,狀態還是執行中容易導致我們隊當前系統進行誤判,程式碼中已經呼叫resume()方法了,但是由於時間先後順序的緣故,resume並沒有生效,這導致了t2永遠滴被掛起了,並且永遠佔用了object的鎖,這對於系統來說可能是致命的。

等待執行緒結束(join)和謙讓(yeild)

很多時候,一個執行緒的輸入可能非常依賴於另外一個或者多個執行緒的輸出,此時,這個執行緒就需要等待依賴的執行緒執行完畢,才能繼續執行。jdk提供了join()操作來實現這個功能。如下所示,顯示了2個join()方法:

public final void join() throws InterruptedException;
public final synchronized void join(long millis) throws InterruptedException;

第1個方法表示無限等待,它會一直只是當前執行緒。知道目標執行緒執行完畢。

第2個方法有個引數,用於指定等待時間,如果超過了給定的時間目標執行緒還在執行,當前執行緒也會停止等待,而繼續往下執行。

比如:執行緒T1需要等待T2、T3完成之後才能繼續執行,那麼在T1執行緒中需要分別呼叫T2和T3的join()方法。

上個示例:

/**
 * <b>description</b>: <br>
 * <b>time</b>:2019/7/12 17:18 <br>
 * <b>author</b>:微信公眾號:路人甲Java,專注於java技術分享(帶你玩轉 爬蟲、分散式事務、非同步訊息服務、任務排程、分庫分表、大資料等),喜歡請關注!
 */
public class Demo08 {
    static int num = 0;

    public static class T1 extends Thread {
        public T1(String name) {
            super(name);
        }

        @Override
        public void run() {
            System.out.println(System.currentTimeMillis() + ",start " + this.getName());
            for (int i = 0; i < 10; i++) {
                num++;
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(System.currentTimeMillis() + ",end " + this.getName());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        T1 t1 = new T1("t1");
        t1.start();
        t1.join();
        System.out.println(System.currentTimeMillis() + ",num = " + num);
    }
}

執行結果:

1562939889129,start t1
1562939891134,end t1
1562939891134,num = 10

num的結果為10,1、3行的時間戳相差2秒左右,說明主執行緒等待t1完成之後才繼續執行的。

看一下jdk1.8中Thread.join()方法的實現:

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的程式碼中可以看出,在被等待的執行緒上使用了synchronize,呼叫了它的wait()方法,執行緒最後執行完畢之後,系統會自動呼叫它的notifyAll()方法,喚醒所有在此執行緒上等待的其他執行緒。

注意:被等待的執行緒執行完畢之後,系統自動會呼叫該執行緒的notifyAll()方法。所以一般情況下,我們不要去線上程物件上使用wait()、notify()、notifyAll()方法。

另外一個方法是Thread.yield(),他的定義如下:

public static native void yield();

yield是謙讓的意思,這是一個靜態方法,一旦執行,它會讓當前執行緒出讓CPU,但需要注意的是,出讓CPU並不是說不讓當前執行緒執行了,當前執行緒在出讓CPU後,還會進行CPU資源的爭奪,但是能否再搶到CPU的執行權就不一定了。因此,對Thread.yield()方法的呼叫好像就是在說:我已經完成了一些主要的工作,我可以休息一下了,可以讓CPU給其他執行緒一些工作機會了。

如果覺得一個執行緒不太重要,或者優先順序比較低,而又擔心此執行緒會過多的佔用CPU資源,那麼可以在適當的時候呼叫一下Thread.yield()方法,給與其他執行緒更多的機會。

總結

  1. 建立執行緒的2中方式:繼承Thread類;實現Runnable介面
  2. 啟動執行緒:呼叫執行緒的start()方法
  3. 終止執行緒:呼叫執行緒的stop()方法,方法已過時,建議不要使用
  4. 執行緒中斷相關的方法:呼叫執行緒例項interrupt()方法將中斷標誌置為true;使用執行緒例項方法isInterrupted()獲取中斷標誌;呼叫Thread的靜態方法interrupted()獲取執行緒是否被中斷,此方法呼叫之後會清除中斷標誌(將中斷標誌置為false了)
  5. wait、notify、notifyAll方法,這塊比較難理解,可以回過頭去再理理
  6. 執行緒掛起使用執行緒例項方法suspend(),恢復執行緒使用執行緒例項方法resume(),這2個方法都過時了,不建議使用
  7. 等待執行緒結束:呼叫執行緒例項方法join()
  8. 出讓cpu資源:呼叫執行緒靜態方法yeild()

java高併發系列交流群

相關推薦

java併發系列 - 6:執行基本操作

新建執行緒 新建執行緒很簡單。只需要使用new關鍵字建立一個執行緒物件,然後呼叫它的start()啟動執行緒即可。 Thread thread1 = new Thread1(); t1.start(); 那麼執行緒start()之後,會幹什麼呢?執行緒有個run()方法,start()會建立一個新的執行緒並讓

java併發系列 - 31:獲取執行執行結果,這6種方法你都知道?

這是java高併發系列第31篇。 環境:jdk1.8。 java高併發系列已經學了不少東西了,本篇文章,我們用前面學的知識來實現一個需求: 在一個執行緒中需要獲取其他執行緒的執行結果,能想到幾種方式?各有什麼優缺點? 結合這個需求,我們使用6種方式,來對之前學過的知識點做一個回顧,加深記憶。 方式1:Thre

java併發系列 - 16:JUC中等待多執行完成的工具類CountDownLatch,必備技能

這是java高併發系列第16篇文章。 本篇內容 介紹CountDownLatch及使用場景 提供幾個示例介紹CountDownLatch的使用 手寫一個並行處理任務的工具類 假如有這樣一個需求,當我們需要解析一個Excel裡多個sheet的資料時,可以考慮使用多執行緒,每個執行緒解析一個sheet裡的資料

java併發系列 - 17:JUC中的迴圈柵欄CyclicBarrier常見的6種使用場景及程式碼示例

這是java高併發系列第17篇。 本文主要內容: 介紹CyclicBarrier 6個示例介紹CyclicBarrier的使用 對比CyclicBarrier和CountDownLatch CyclicBarrier簡介 CyclicBarrier通常稱為迴圈屏障。它和CountDownLatch很相似,

java併發系列-1:必須知道的幾個概念

java高併發系列-第1天:必須知道的幾個概念 同步(Synchronous)和非同步(Asynchronous) 同步和非同步通常來形容一次方法呼叫,同步方法呼叫一旦開始,呼叫者必須等到方法呼叫返回後,才能繼續後續的行為。非同步方法呼叫更像一個訊息傳遞,一旦開始,方法呼叫就會立即返回,呼叫者就可以繼續後續的

java併發系列 - 12JUC:ReentrantLock重入鎖

java高併發系列 - 第12天JUC:ReentrantLock重入鎖 本篇文章開始將juc中常用的一些類,估計會有十來篇。 synchronized的侷限性 synchronized是java內建的關鍵字,它提供了一種獨佔的加鎖方式。synchronized的獲取和釋放鎖由jvm實現,使用者不需要顯示的釋

java併發系列 - 14:JUC中的LockSupport工具類,必備技能

這是java高併發系列第14篇文章。 本文主要內容: 講解3種讓執行緒等待和喚醒的方法,每種方法配合具體的示例 介紹LockSupport主要用法 對比3種方式,瞭解他們之間的區別 LockSupport位於java.util.concurrent(簡稱juc)包中,算是juc中一個基礎類,juc中很多地

java併發系列 - 15:JUC中的Semaphore,最簡單的限流工具類,必備技能

這是java高併發系列第15篇文章 Semaphore(訊號量)為多執行緒協作提供了更為強大的控制方法,前面的文章中我們學了synchronized和重入鎖ReentrantLock,這2種鎖一次都只能允許一個執行緒訪問一個資源,而訊號量可以控制有多少個執行緒可以同時訪問特定的資源。 Semaphore常用

java併發系列 - 21java中的CAS操作java併發的基石

這是java高併發系列第21篇文章。 本文主要內容 從網站計數器實現中一步步引出CAS操作 介紹java中的CAS及CAS可能存在的問題 悲觀鎖和樂觀鎖的一些介紹及資料庫樂觀鎖的一個常見示例 使用java中的原子操作實現網站計數器功能 我們需要解決的問題 需求:我們開發了一個網站,需要對訪問量進行統計,使

java併發系列 - 22java中底層工具類Unsafe,高手必須要了解

這是java高併發系列第22篇文章,文章基於jdk1.8環境。 本文主要內容 基本介紹 通過反射獲取Unsafe例項 Unsafe中的CAS操作 Unsafe中原子操作相關方法介紹 Unsafe中執行緒排程相關方法 park和unpark示例 Unsafe鎖示例 Unsafe中保證變數的可見性 Unsafe

java併發系列 - 23:JUC中原子類,一篇就夠了

這是java高併發系列第23篇文章,環境:jdk1.8。 本文主要內容 JUC中的原子類介紹 介紹基本型別原子類 介紹陣列型別原子類 介紹引用型別原子類 介紹物件屬性修改相關原子類 預備知識 JUC中的原子類都是都是依靠volatile、CAS、Unsafe類配合來實現的,需要了解的請移步: volati

java併發系列 - 24:ThreadLocal、InheritableThreadLocal(通俗易懂)

java高併發系列第24篇文章。 環境:jdk1.8。 本文內容 需要解決的問題 介紹ThreadLocal 介紹InheritableThreadLocal 需要解決的問題 我們還是以解決問題的方式來引出ThreadLocal、InheritableThreadLocal,這樣印象會深刻一些。 目前

java併發系列 - 25:掌握JUC中的阻塞佇列

這是java高併發系列第25篇文章。 環境:jdk1.8。 本文內容 掌握Queue、BlockingQueue介面中常用的方法 介紹6中阻塞佇列,及相關場景示例 重點掌握4種常用的阻塞佇列 Queue介面 佇列是一種先進先出(FIFO)的資料結構,java中用Queue介面來表示佇列。 Queue介面中

java併發系列 - 27:實戰篇,介面效能成倍提升,讓同事刮目相看,現學現用

這是java高併發系列第27篇文章。 開發環境:jdk1.8。 案例講解 電商app都有用過吧,商品詳情頁,需要給他們提供一個介面獲取商品相關資訊: 商品基本資訊(名稱、價格、庫存、會員價格等) 商品圖片列表 商品描述資訊(描述資訊一般是由富文字編輯的大文字資訊) 資料庫中我們用了3張表儲存上面的資訊:

java併發系列 - 32併發中計數器的實現方式有哪些?

這是java高併發系列第32篇文章。 java環境:jdk1.8。 本文主要內容 4種方式實現計數器功能,對比其效能 介紹LongAdder 介紹LongAccumulator 需求:一個jvm中實現一個計數器功能,需保證多執行緒情況下資料正確性。 我們來模擬50個執行緒,每個執行緒對計數器遞增100萬次

實戰Java併發程式設計(3.2 執行池)

1.Executor jdk提供了一套Executor框架,本質上是一個執行緒池。 newFixedThreadPool()方法:該方法返回一個固定數量的執行緒池。該執行緒池中的執行緒數量始終不變,當有一個新任務提交時,執行緒池中若有空閒執行緒,則立即執行,若沒有,則任務會暫存在一個任

JAVA併發(二)------區分執行和程序

執行緒與程序 程序是計算機中的程式關於某資料集合上的一次執行活動,是系統進行資源分配和排程的基本單位,是作業系統結構的基礎,在早期面向程序設計的計算機結構中,程序是程式的基本執行實體,在當代面向執行緒的計算機結構中,程序是執行緒的容器,程式是指令資料及其組織形式的描述,程序

java併發系列 - 29併發中常見的限流方式

這是java高併發系列第29篇。 環境:jdk1.8。 本文內容 介紹常見的限流演算法 通過控制最大併發數來進行限流 通過漏桶演算法來進行限流 通過令牌桶演算法來進行限流 限流工具類RateLimiter 常見的限流的場景 秒殺活動,數量有限,訪問量巨大,為了防止系統宕機,需要做限流處理 國慶期間,一般

JAVA入門到精通-38講-執行-坦克大戰6

 應用程式在記憶體中開闢空間-程序;  記憶體-大腦容量;     反應切換的速度-CPU;  試圖去嘗試別的程序的地址空間---病毒;  

Go 系列教程-6執行 併發

Go 系列教程 —— 20. 併發入門 Go 是併發式語言,而不是並行式語言。在討論 Go 如何處理併發之前,我們必須理解何為併發,以及併發與並行的區別。 併發是什麼? 併發是指立即處理多個任務的能力。一個例子就能很好地說明這一點。 我們可以想象一個人正在跑步。假如在他晨跑時,鞋帶突