1. 程式人生 > >Java每天一個知識點+Demo-多執行緒相關命令

Java每天一個知識點+Demo-多執行緒相關命令

一 多執行緒相關命令介紹

1 wait()  sleep()

 (1)這兩個方法來自不同的類分別是wait()來自Thread,sleep()來自Object。

 (2) 最主要是sleep方法沒有釋放鎖,sleep使當前執行緒進入停滯狀態(阻塞當前執行緒),讓出cpu的使用、目的是不讓當前執行緒   獨自霸佔該程序所獲的CPU資源,以留一定時間給其他執行緒執行的機會執行緒雖然休眠了,但是物件的機鎖並木有被釋放,其         他執行緒無法訪問這個物件(即使睡著也持有物件鎖)。

      而wait方法釋放了鎖,使得其他執行緒可以使用同步控制塊或者方法。當一個執行緒執行到wait()方法時,它就進入到一個和該對   象相關的等待池中,同時失去(釋放)了物件鎖(暫時失去物件鎖,wait(long timeout)超時時間到後還需要返還物件鎖),從而    其他執行緒可以訪問;

 (3) wait,notify和notifyAll只能在同步控制方法或者同步控制塊裡面使用,而sleep可以在任何地方使用

               synchronized(x){

                         x.notify()

                       //或者wait()

               }

    synchronized程式碼被執行期間,執行緒可以呼叫物件的wait()方法,釋放物件鎖標誌,進入等待狀態。

   (4) sleep必須捕獲異常,而wait,notify和notifyAll不需要捕獲異常。

2 notify()  notifyAll()

(1) wait()使用notify或者notifyAlll或者指定睡眠時間來喚醒當前等待池中的執行緒。

(2) notify()通知等待佇列中的第一個執行緒,notifyAll()通知的是等待佇列中的所有執行緒。

3 join()

    等待該執行緒終止。

    等待呼叫join()方法的執行緒結束。比如t.join(),用於等待t執行緒執行結束。

4 yield()

暫停當前正在執行的執行緒。

yield()的作用是讓步。它能讓當前執行緒由“執行狀態”進入到“就緒狀態”,從而讓其它具有相同優先順序的等待執行緒獲取執行權;但是,並不能保證在當前執行緒呼叫yield()之後,其它具有相同優先順序的執行緒就一定能獲得執行權;也有可能是當前執行緒又進入到“執行狀態”繼續執行。

二 Demo

1 wait()  sleep()

public class ThreadDemo implements Runnable{
    
    public void do1() throws Exception{
        synchronized(this){
            
             System.out.println("11111111111111111");
        }
    }
    
    public void do2() throws Exception{
        synchronized(this){
            //Thread.sleep(2000);
            this.wait();
            System.out.println("22222222222222222");
        }
    }
    
    @Override
    public void run(){
        try{
            do1();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) throws Exception {
        ThreadDemo threadDemo = new ThreadDemo();
        Thread t = new Thread(threadDemo);
        t.start();
        threadDemo.do2();
    }
    
}

執行結果:

假如執行的是this.wait(),結果是

11111111111111111

假如執行的是Thread.sleep(2000),結果是

22222222222222222
11111111111111111

分析:

在main()方法裡(在主執行緒)開啟了一個子執行緒(t.start()),接著執行了方法do2()。自此,主執行緒和子執行緒交替輪流執行,由於子執行緒剛啟動,所以do2()方法會先被執行。必須注意的是,在方法do1()和do2()中都使用了同步鎖(synchronized(this)),且都是同一個物件鎖,而do1()執行在子執行緒,do2()執行在主執行緒,可想而知,主執行緒和子執行緒可能會因為同一個物件鎖而發生阻塞。

好的,再來看具體的程式碼。假如執行的是this.wait(),那麼主執行緒中的do2()走到this.wait(),執行緒方法wait()會使當前執行緒也就是主執行緒進入等待池中也就是執行緒停滯了,那麼其它執行緒有機會得到cpu排程,而且放棄了物件鎖,那麼使得其它執行緒能夠得到該物件鎖從而能夠執行加了同步鎖的程式碼塊。(特別注意這裡,執行緒進入等待池和放棄同步鎖帶來不一樣的東西)由於主執行緒停滯,所以子執行緒得以執行,由於主執行緒放棄了同步鎖,所以子執行緒執行到do1()方法時能夠進入同步程式碼塊,打印出結果。

假如執行的是Thread.sleep(2000),那麼同樣首先主執行緒do2()走到Thread.sleep(2000),那麼此時主執行緒就開始睡眠,但是睡眠的同時也並沒有放棄鎖,導致的結果是雖然主執行緒睡眠讓子執行緒有被排程的機會但是主執行緒還保持著同步鎖所以讓子執行緒無法執行do1()裡的同步程式碼塊,所以結果是要一直等到2s結束,主執行緒重新得到排程執行完do2()後放棄了鎖,子執行緒再執行do1()。

2 wait()  notify()  notifyAll()

wait()與notify()

public class ThreadDemo {
    
static class ThreadA extends Thread{
   public ThreadA(String name) {
       super(name);
   }
   public void run() {
       synchronized (this) {
           System.out.println(Thread.currentThread().getName()+"  notify()");
           // 喚醒當前的wait執行緒
           notify();
       }
   }
}

public static void main(String[] args) {
        ThreadA t1 = new  ThreadA("t1");
        synchronized(t1) {
            try {
                // 啟動執行緒t1
                t1.start();


                // 主執行緒進入等待池
                System.out.println(Thread.currentThread().getName()+" wait()");
                t1.wait();

               //主執行緒等待t1通過notify()喚醒
                System.out.println(Thread.currentThread().getName()+" continue");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

執行結果:

main wait()
t1  notify()
main continue

分析:

主執行緒通過 new ThreadA("t1") 新建執行緒t1 ,隨後通過synchronized(t1)獲取t1物件的同步鎖,然後呼叫t1.start()啟動執行緒t1。
主執行緒執行t1.wait() 釋放t1物件的鎖並且進入等待(阻塞)狀態。等待t1物件上的執行緒通過notify() 或 notifyAll()將其喚醒。
執行緒t1執行之後,通過synchronized(this)獲取當前物件的鎖;接著呼叫notify()喚醒當前物件上的等待執行緒,也就是喚醒主執行緒。
執行緒t1執行完畢之後,釋放當前物件的鎖,緊接著,主執行緒獲取t1物件的鎖,然後接著執行。


* 注意jdk的解釋中,說wait()的作用是讓“當前執行緒”等待,而“當前執行緒”是指正在cpu上執行的執行緒。
這也意味著,雖然t1.wait()是通過“執行緒t1”呼叫的wait()方法,但是呼叫t1.wait()的地方是在主執行緒main中。而主執行緒必須是當前執行緒,也就是執行狀態,才可以執行t1.wait()。所以,此時的“當前執行緒是主執行緒main。因此,t1.wait()是讓主執行緒等待,而不是執行緒t1。

wait()與notifyAll()

public class ThreadDemo {
    private static Object obj = new Object();
    
    public static void main(String[] args) {
        ThreadA t1 = new ThreadA("t1");
        ThreadA t2 = new ThreadA("t2");
        ThreadA t3 = new ThreadA("t3");
        t1.start();
        t2.start();
        t3.start();
        try {
        System.out.println(Thread.currentThread().getName()+ " sleep(3000)");
        Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (obj) {
        // 喚醒執行緒
        System.out.println(Thread.currentThread().getName() + " notifyAll()");
        obj.notifyAll();
        }
    }
    
    static class ThreadA extends Thread {
        public ThreadA(String name) {
            super(name);
        }
        public void run() {
            synchronized (obj) {
                try {
                    // 執行緒進入等待池
                    System.out.println(Thread.currentThread().getName() + " wait");
                     obj.wait();


                    // 喚醒當前的wait執行緒
                    System.out.println(Thread.currentThread().getName() + " continue");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

執行結果:

t1 wait
main sleep    (3000)
t3 wait
t2 wait
main notifyAll()
t2 continue
t3 continue
t1 continue

分析:

主執行緒中新建並且啟動了3個執行緒t1, t2和t3。
主執行緒通過sleep(3000)休眠3秒。在主執行緒休眠3秒的過程中,我們假設t1, t2和t3這3個執行緒都運行了。以t1為例,當它執行的時候,它會執行obj.wait()等待其它執行緒通過notify()或額nofityAll()來喚醒它;相同的道理,t2和t3也會等待其它執行緒通過nofity()或nofityAll()來喚醒它們。
主執行緒休眠3秒之後,接著執行。執行 obj.notifyAll() 喚醒obj上的等待執行緒,即喚醒t1, t2和t3這3個執行緒。 緊接著,主執行緒的synchronized(obj)執行完畢之後,主執行緒釋放obj鎖。這樣,t1, t2和t3就可以獲取obj鎖而繼續運行了。
 

3 join()

public class ThreadDemo extends Thread {

    public ThreadDemo(String name){
        super(name);
    }

    @Override
    public void run(){
        for(int i=1;i<5;i++){
            System.out.println(this.getName() + "    do" + i);
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        ThreadDemo t=new ThreadDemo("子執行緒");
        t.start();
        //呼叫t執行緒的join方法,等待t執行緒執行完畢
        t.join();
        System.out.println("主執行緒");
    }
}

執行結果:

子執行緒 do1
子執行緒 do2
子執行緒 do3
子執行緒 do4
主執行緒

分析:

主執行緒中新建並且啟動了執行緒t,呼叫t.join(),主執行緒會等t執行緒執行完再執行。

4 yield()

public class ThreadDemo {
    
    public static void main(String[] args) {
        YieldThread t1 = new YieldThread("執行緒A");
        YieldThread t2 = new YieldThread("執行緒B");
        t1.start();
        t2.start();
    }
    
   static class YieldThread extends Thread {
        private String name;

        public YieldThread(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            for (int i = 1; i <= 4; i++) {
                System.out.println(name + "   " + i);
                if (i == 2) {
                 Thread.yield();
                }
           }
       }
    }
}

執行結果:

執行緒A   1
執行緒A   2
執行緒B   1
執行緒B   2
執行緒B   3
執行緒B   4
執行緒A   3
執行緒A   4

分析:

這只是其中之一的一個執行結果,隨機會有好幾種執行結果。當i==2時,執行Thread.yield()會暫停當前執行緒,進入就緒狀態,不過也有可能該執行緒再次進入執行狀態,這也是導致上述demo有多種執行結果的原因之一。

三 總結&注意

(1) 在Java.lang.Thread類中,提供了sleep(),而java.lang.Object類中提供了wait(), notify()和notifyAll()方法來操作執行緒

(2) Thread.sleep()方法是一個靜態方法,它暫停的是當前執行的執行緒。

(3) wait方法必須正在同步環境下使用,比如synchronized方法或者同步程式碼塊。如果不在同步條件下使用,會丟擲IllegalMonitorStateException異常。

(4)注意持有鎖和cpu排程的區別,執行緒持有鎖代表擁有該鎖物件對應的資源的使用權,其它執行緒再無法使用該資源;而cpu排程是cpu排程策略來控制的,與是否持鎖並無關係。比如執行sleep時持有鎖但不會被cpu排程。

很多知識都是通過前輩部落格整理而來,前輩比較好的程式碼demo也會借鑑用來展示說明,比如介紹比較全面的執行緒相關命令介紹

相關推薦

Java每天一個知識點+Demo-執行相關命令

一 多執行緒相關命令介紹 1 wait()  sleep()  (1)這兩個方法來自不同的類分別是wait()來自Thread,sleep()來自Object。  (2) 最主要是sleep方法沒有釋放鎖,sleep使當前執行緒進入停滯狀態(阻塞當前執行緒),讓出cpu

Java知識點24——執行技術

多執行緒技術 三高:高可用、高效能、高併發 基本概念:多執行緒是Java語言的重要特性,大量應用於網路程式設計、伺服器端程式的開發,最常見的UI介面底層原理、作業系統底層原理都大量使用了多執行緒。 我們可以流暢的點選軟體或者遊戲中的各種按鈕,其實,底層就是多執行緒的應用。UI介面的主執行

java執行相關知識點

1.sleep和wait的區別 (1).sleep是Thread中的靜態方法,誰呼叫誰去睡覺,即使在T1執行緒裡呼叫了T2執行緒的sleep方法, 實際上還是T1去睡覺。 (2)sleep方法不會釋放物件得鎖,而wait方法釋放了鎖. (3)wait,notify和notifyAll只

java面試/筆試題目之執行及鎖 (持續更新中)

前言:這一模組可以參照徐劉根大佬的部落格。 一.執行緒和程序的概念、並行和併發的概念 1.程序:是計算機中的程式關於某資料集合上的一次執行活動,是系統 進行資源分配和排程的基本單位,是作業系統結構的基礎。程式是指令、資料及其組織形式的描述,程序是程式的實體。 2.執行緒:是程式執行流的

【紮實基本功】Java基礎教程系列之執行

1. 多執行緒的概念 1.1 程序、執行緒、多程序的概念 程序:正在進行中的程式(直譯)。 執行緒是程式執行的一條路徑, 一個程序中可以包含多條執行緒。 一個應用程式可以理解成就是一個程序。 多執行緒併發執行可以提高程式的效率, 可以同時完成多項工作。 1.

java最簡單粗暴講解執行,還不趕緊上車!

這裡並沒有講什麼新東西,只是把多執行緒一些知識來個總結。大家懂得可以複習複習,還有些童鞋對多執行緒朦朧的可以拿這個做為入門~ 舉個栗子說明啥是多執行緒:玩遊戲,前面一堆怪,每個怪都是一個執行緒,你射了一槍,子彈飛出去了,這顆子彈也是一個執行緒。你開啟你的程序管理,看到你遊戲的後臺程序,這就是程序

java 利用Future非同步獲取執行任務結果

Future介面是Java標準API的一部分,在java.util.concurrent包中。Future介面是Java執行緒Future模式的實現,可以來進行非同步計算。 有了Future就可以進行三段式的程式設計了,1.啟動多執行緒任務2.處理其他事3.收集多執行緒任務結果。從而實現了非阻塞

Android每天一個知識點+Demo—非同步訊息機制實現

一 前言  Handler的由來 (1)首先為什麼需要Handler呢?                 因為UI更新只能在UI執行緒。  (2)那為什麼只能在UI執行緒更新UI呢?        因為Android是單執行緒模型。  (3)那為什麼Android是單執行

Android每天一個知識點+Demo—跨程序通訊機制AIDL入門

一 Why-為什麼要用AIDL 沙箱理念:在Android中,每個應用(Application)程式都執行在獨立的程序中,無法直接呼叫到其他應用的資源。當一個應用被執行時,一些操作是被限制的,比如訪問記憶體,訪問感測器等等。 好處:這也保證了當其中一個程式出現異常而不會影

Android小知識-Java執行相關執行間通訊)下篇

本平臺的文章更新會有延遲,大家可以關注微信公眾號-顧林海,如果大家想獲取最新教程,請關注微信公眾號,謝謝! 在一種場景中,比如main執行緒建立並啟動了子執行緒,子執行緒需要做耗時操作,如果main執行緒想等子執行緒執行完成之後再結束,由於main執行緒比子執行緒先執行完,因此main執行緒獲取不

JAVA 8 併發增強(1) 執行修改某個計數器的方式

Q:如何正確的併發修改一個AtomicLong的值? /* 不同執行緒檢測最大值 */ AtomicLong largest = new AtomicLong(); long obsvValue = 0; /* 錯誤的方式,此更新不是原子性的 */ largest.

胡八一之Java(八):執行

多執行緒的優勢:多程序執行需要獨立的記憶體空間,而多執行緒可以共享記憶體,從而提高了執行緒的執行效率。 建立執行緒一般使用兩種方式: 1、繼承Thread類: import java.io.IOException; public class Test extends

java-雙重檢查鎖為什麼執行不安全

如下程式碼所示: public class doubleCheck{ private static Instance instance; public static Instance getInstance(){ if(instance==null){ //1

java高併發學習(九)-----執行的團隊協作:同步控制

Java高併發學習(八)-------多執行緒的團隊協作:同步控制   同步控制是併發程式必不可少的重要手段。之前介紹的synchronized關鍵字就是一種最簡單的控制方法。同時,wait()和notify()方法起到了執行緒等待和通知的作用。這些工具對於實現複雜的多

秒殺執行第四篇 一個經典的執行同步問題

上一篇《秒殺多執行緒第三篇原子操作 Interlocked系列函式》中介紹了原子操作在多程序中的作用,現在來個複雜點的。這個問題涉及到執行緒的同步和互斥,是一道非常有代表性的多執行緒同步問題,如果能將這個問題搞清楚,那麼對多執行緒同步也就打下了良好的基礎。程式描述:主執行緒啟

linux下一個程序中執行的資源共享

在說執行緒資源共享之前,我們先來說來說一下執行緒的概念,執行緒是程序內部的一條執行序列(即執行流),一個程序至少有一個執行緒,即main函式代表的執行流。當然我們也可以通過執行緒庫來建立新的執行緒,這種執行緒我們稱之為函式執行緒,同一個程序中的所有普執行緒是併發執行的。而這些

Java執行的實現(程序與執行的概念、Java繼承Thread類實現執行Java實現Runnable介面實現執行、Thread與Runnable的區別、實現Callable介面實現執行

1 程序與執行緒 1.1 程序與執行緒的概念 什麼是程序?   程序: 作業系統中一個程式的執行週期。(比如我們想要在電腦上登入QQ,從雙擊qq按鈕---->關閉qq這個過程就是一個程序)   多程序: 同一時刻跑多個程式。   在DOS(磁碟作業系統時

Java 使用阻塞佇列 BlockingQueue 執行搜尋目錄及子目錄下包含關鍵字所有檔案

Java 使用阻塞佇列 BlockingQueue 多執行緒在一個目錄及它的所以子目錄下搜尋所有檔案,打印出包含關鍵字的行 阻塞佇列( blocking queue ) 生產者執行緒向佇列插人元素, 消費者執行緒則取出它們。使用佇列,可以安全地從一個執行緒向另一個執行緒傳遞資料。

Java 執行 相關概念

前言 本篇文章介紹一些多執行緒的相關的深入概念。理解後對於執行緒的安全性會有更深的理解。 先說一個格言,摘自Java核心技術: 如果向一個變數寫入值,而這個變數接下來可能會被另一個執行緒讀取;或者一個變數讀值,而這個變數可能是之前被另一個執行緒寫入的,此時必須同步。 下面就是概念了。

學了Java併發程式設計藝術及執行核心程式設計技術,以及最開始學的程式設計思想那本書,今天做些總結

併發Map分析位碼shift預設值是28,對hash值右移28位,取高四位,獲得segments位置,掩碼mask預設值16-1,作一個與值,不知道有何用處,兩個都是不可修改,初始值和併發度有關,一旦確立下來決定了segments陣列大小,包括segments陣列物件不可修改