黄色网址大全免费-黄色网址你懂得-黄色网址你懂的-黄色网址有那些-免费超爽视频-免费大片黄国产在线观看

專注Java教育14年 全國咨詢/投訴熱線:400-8080-105
動力節(jié)點LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁 學(xué)習(xí)攻略 java多線程的狀態(tài)轉(zhuǎn)換以及基本操作

java多線程的狀態(tài)轉(zhuǎn)換以及基本操作

更新時間:2019-08-26 15:22:10 來源:動力節(jié)點 瀏覽3064次



今天動力節(jié)點java學(xué)院小編分享如何新建一個線程?線程狀態(tài)是怎樣轉(zhuǎn)換的?關(guān)于線程狀態(tài)的操作是怎樣的?下面各位小伙伴就隨小編一起來看看java多線程的狀態(tài)轉(zhuǎn)換以及基本操作吧。


1、新建線程


  一個java程序從main()方法開始執(zhí)行,然后按照既定的代碼邏輯執(zhí)行,看似沒有其他線程參與,但實際上java程序天生就是一個多線程程序,包含了:


(1)分發(fā)處理發(fā)送給給JVM信號的線程;


(2)調(diào)用對象的finalize方法的線程;


(3)清除Reference的線程;


(4)main線程,用戶程序的入口。


那么,如何在用戶程序中新建一個線程了,只要有三種方式:


  A:通過繼承Thread類,重寫run方法;


  B:通過實現(xiàn)runable接口;


  C:通過實現(xiàn)callable接口這三種方式,下面看具體demo。


ublic class CreateThreadDemo {


     public static void main(String[] args) {

         //1.繼承Thread

         Thread thread = new Thread() {

             @Override

             public void run() {

                 System.out.println("繼承Thread");

                 super.run();

             }

         };

         thread.start();

         //2.實現(xiàn)runable接口

         Thread thread1 = new Thread(new Runnable() {

             @Override

             public void run() {

                 System.out.println("實現(xiàn)runable接口");

             }

         });

         thread1.start();

         //3.實現(xiàn)callable接口

         ExecutorService service = Executors.newSingleThreadExecutor();

         Future<String> future = service.submit(new Callable() {

             @Override

             public String call() throws Exception {

                 return "通過實現(xiàn)Callable接口";

             }

         });

         try {

             String result = future.get();

             System.out.println(result);

         } catch (InterruptedException e) {

             e.printStackTrace();

         } catch (ExecutionException e) {

             e.printStackTrace();

         }

     }


 }


  三種新建線程的方式具體看以上注釋,需要注意的是:


  由于java不能多繼承可以實現(xiàn)多個接口,因此,在創(chuàng)建線程的時候盡量多考慮采用實現(xiàn)接口的形式;


  實現(xiàn)callable接口,提交給ExecutorService返回的是異步執(zhí)行的結(jié)果,另外,通常也可以利用FutureTask(Callable callable)將callable進(jìn)行包裝然后FeatureTask提交給ExecutorsService。如圖,


  另外由于FeatureTask也實現(xiàn)了Runable接口也可以利用上面第二種方式(實現(xiàn)Runable接口)來新建線程;


  可以通過Executors將Runable轉(zhuǎn)換成Callable,具體方法是:Callable callable(Runnable task, T result), Callable callable(Runnable task)。



  2、線程狀態(tài)轉(zhuǎn)換


1.png

  此圖來源于《JAVA并發(fā)編程的藝術(shù)》一書中,線程是會在不同的狀態(tài)間進(jìn)行轉(zhuǎn)換的,java線程線程轉(zhuǎn)換圖如上圖所示。線程創(chuàng)建之后調(diào)用start()方法開始運行,當(dāng)調(diào)用wait(),join(),LockSupport.lock()方法線程會進(jìn)入到WAITING狀態(tài),而同樣的wait(long timeout),sleep(long),join(long),LockSupport.parkNanos(),LockSupport.parkUtil()增加了超時等待的功能,也就是調(diào)用這些方法后線程會進(jìn)入TIMED_WAITING狀態(tài),當(dāng)超時等待時間到達(dá)后,線程會切換到Runable的狀態(tài),另外當(dāng)WAITING和TIMED _WAITING狀態(tài)時可以通過Object.notify(),Object.notifyAll()方法使線程轉(zhuǎn)換到Runable狀態(tài)。當(dāng)線程出現(xiàn)資源競爭時,即等待獲取鎖的時候,線程會進(jìn)入到BLOCKED阻塞狀態(tài),當(dāng)線程獲取鎖時,線程進(jìn)入到Runable狀態(tài)。線程運行結(jié)束后,線程進(jìn)入到TERMINATED狀態(tài),狀態(tài)轉(zhuǎn)換可以說是線程的生命周期。另外需要注意的是:


當(dāng)線程進(jìn)入到synchronized方法或者synchronized代碼塊時,線程切換到的是BLOCKED狀態(tài),而使用java.util.concurrent.locks下lock進(jìn)行加鎖的時候線程切換的是WAITING或者TIMED_WAITING狀態(tài),因為lock會調(diào)用LockSupport的方法。


  用一個表格將上面六種狀態(tài)進(jìn)行一個總結(jié)歸納。


1566802491490699.png



  3、 線程狀態(tài)的基本操作


  除了新建一個線程外,線程在生命周期內(nèi)還有需要基本操作,而這些操作會成為線程間一種通信方式,比如使用中斷(interrupted)方式通知實現(xiàn)線程間的交互等等,下面將具體說說這些操作。


  (1) interrupted


  中斷可以理解為線程的一個標(biāo)志位,它表示了一個運行中的線程是否被其他線程進(jìn)行了中斷操作。中斷好比其他線程對該線程打了一個招呼。其他線程可以調(diào)用該線程的interrupt()方法對其進(jìn)行中斷操作,同時該線程可以調(diào)用 isInterrupted()來感知其他線程對其自身的中斷操作,從而做出響應(yīng)。另外,同樣可以調(diào)用Thread的靜態(tài)方法 interrupted()對當(dāng)前線程進(jìn)行中斷操作,該方法會清除中斷標(biāo)志位。需要注意的是,當(dāng)拋出InterruptedException時候,會清除中斷標(biāo)志位,也就是說在調(diào)用isInterrupted會返回false。


1566802604361423.png


下面結(jié)合具體的實例來看一看


public class InterruptDemo {

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

            //sleepThread睡眠1000ms

            final Thread sleepThread = new Thread() {

                @Override

                public void run() {

                    try {

                        Thread.sleep(1000);

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                    super.run();

                }

            };

            //busyThread一直執(zhí)行死循環(huán)

            Thread busyThread = new Thread() {

                @Override

                public void run() {

                    while (true) ;

                }

            };

            sleepThread.start();

            busyThread.start();

            sleepThread.interrupt();

            busyThread.interrupt();

            while (sleepThread.isInterrupted()) ;

            System.out.println("sleepThread isInterrupted: " + sleepThread.isInterrupted());

            System.out.println("busyThread isInterrupted: " + busyThread.isInterrupted());

        }

    }


輸出結(jié)果


> sleepThread isInterrupted: false busyThread isInterrupted: true



  開啟了兩個線程分別為sleepThread和BusyThread, sleepThread睡眠1s,BusyThread執(zhí)行死循環(huán)。然后分別對著兩個線程進(jìn)行中斷操作,可以看出sleepThread拋出InterruptedException后清除標(biāo)志位,而busyThread就不會清除標(biāo)志位。


  另外,同樣可以通過中斷的方式實現(xiàn)線程間的簡單交互, while (sleepThread.isInterrupted()) 表示在Main中會持續(xù)監(jiān)測sleepThread,一旦sleepThread的中斷標(biāo)志位清零,即sleepThread.isInterrupted()返回為false時才會繼續(xù)Main線程才會繼續(xù)往下執(zhí)行。因此,中斷操作可以看做線程間一種簡便的交互方式。一般在結(jié)束線程時通過中斷標(biāo)志位或者標(biāo)志位的方式可以有機(jī)會去清理資源,相對于武斷而直接的結(jié)束線程,這種方式要優(yōu)雅和安全。



  (2) join


  join方法可以看做是線程間協(xié)作的一種方式,很多時候,一個線程的輸入可能非常依賴于另一個線程的輸出,這就像兩個好基友,一個基友先走在前面突然看見另一個基友落在后面了,這個時候他就會在原處等一等這個基友,等基友趕上來后,就兩人攜手并進(jìn)。其實線程間的這種協(xié)作方式也符合現(xiàn)實生活。在軟件開發(fā)的過程中,從客戶那里獲取需求后,需要經(jīng)過需求分析師進(jìn)行需求分解后,這個時候產(chǎn)品,開發(fā)才會繼續(xù)跟進(jìn)。如果一個線程實例A執(zhí)行了threadB.join(),其含義是:當(dāng)前線程A會等待threadB線程終止后threadA才會繼續(xù)執(zhí)行。關(guān)于join方法一共提供如下這些方法:


public final synchronized void join(long millis) 


public final synchronized void join(long millis, int nanos) 


public final void join() throws InterruptedException


Thread類除了提供join()方法外,另外還提供了超時等待的方法,如果線程threadB在等待的時間內(nèi)還沒有結(jié)束的話,threadA會在超時之后繼續(xù)執(zhí)行。join方法源碼關(guān)鍵是:


  while (isAlive()) {


      wait(0);


     }


可以看出來當(dāng)前等待對象threadA會一直阻塞,直到被等待對象threadB結(jié)束后即isAlive()返回false的時候才會結(jié)束while循環(huán),當(dāng)threadB退出時會調(diào)用notifyAll()方法通知所有的等待線程。下面用一個具體的例子來說說join方法的使用:


  public class JoinDemo {

        public static void main(String[] args) {

            Thread previousThread = Thread.currentThread();

            for (int i = 1; i <= 10; i++) {

                Thread curThread = new JoinThread(previousThread);

                curThread.start();

                previousThread = curThread;

            }

        }


        static class JoinThread extends Thread {

            private Thread thread;


            public JoinThread(Thread thread) {

                this.thread = thread;

            }


            @Override

            public void run() {

                try {

                    thread.join();

                    System.out.println(thread.getName() + " terminated.");

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        }

    }


輸出結(jié)果為:


main terminated. 

Thread-0 terminated. 

Thread-1 terminated. 

Thread-2 terminated. 

Thread-3 terminated. 

Thread-4 terminated. 

Thread-5 terminated. 

Thread-6 terminated. 

Thread-7 terminated. 

Thread-8 terminated.


  在上面的例子中一個創(chuàng)建了10個線程,每個線程都會等待前一個線程結(jié)束才會繼續(xù)運行。可以通俗的理解成接力,前一個線程將接力棒傳給下一個線程,然后又傳給下一個線程......



  (3)sleep


  public static native void sleep(long millis)方法顯然是Thread的靜態(tài)方法,很顯然它是讓當(dāng)前線程按照指定的時間休眠,其休眠時間的精度取決于處理器的計時器和調(diào)度器。需要注意的是如果當(dāng)前線程獲得了鎖,sleep方法并不會失去鎖。sleep方法經(jīng)常拿來與Object.wait()方法進(jìn)行比價,這也是面試經(jīng)常被問的地方。


sleep() VS wait()


兩者主要的區(qū)別:


 1)sleep()方法是Thread的靜態(tài)方法,而wait是Object實例方法


2) wait()方法必須要在同步方法或者同步塊中調(diào)用,也就是必須已經(jīng)獲得對象鎖。而sleep()方法沒有這個限制可以在任何地方種使用。另外,wait()方法會釋放占有的對象鎖,使得該線程進(jìn)入等待池中,等待下一次獲取資源。而sleep()方法只是會讓出CPU并不會釋放掉對象鎖;


3)sleep()方法在休眠時間達(dá)到后如果再次獲得CPU時間片就會繼續(xù)執(zhí)行,而wait()方法必須等待Object.notift/Object.notifyAll通知后,才會離開等待池,并且再次獲得CPU時間片才會繼續(xù)執(zhí)行。



  (4)yield


  public static native void yield();這是一個靜態(tài)方法,一旦執(zhí)行,它會是當(dāng)前線程讓出CPU,但是,需要注意的是,讓出的CPU并不是代表當(dāng)前線程不再運行了,如果在下一次競爭中,又獲得了CPU時間片當(dāng)前線程依然會繼續(xù)運行。另外,讓出的時間片只會分配給當(dāng)前線程相同優(yōu)先級的線程。什么是線程優(yōu)先級了?下面就來具體聊一聊。


  現(xiàn)代操作系統(tǒng)基本采用時分的形式調(diào)度運行的線程,操作系統(tǒng)會分出一個個時間片,線程會分配到若干時間片,當(dāng)前時間片用完后就會發(fā)生線程調(diào)度,并等待這下次分配。線程分配到的時間多少也就決定了線程使用處理器資源的多少,而線程優(yōu)先級就是決定線程需要或多或少分配一些處理器資源的線程屬性。


  在Java程序中,通過一個整型成員變量Priority來控制優(yōu)先級,優(yōu)先級的范圍從1~10.在構(gòu)建線程的時候可以通過setPriority(int)方法進(jìn)行設(shè)置,默認(rèn)優(yōu)先級為5,優(yōu)先級高的線程相較于優(yōu)先級低的線程優(yōu)先獲得處理器時間片。需要注意的是在不同JVM以及操作系統(tǒng)上,線程規(guī)劃存在差異,有些操作系統(tǒng)甚至?xí)雎跃€程優(yōu)先級的設(shè)定。


  另外需要注意的是,sleep()和yield()方法,同樣都是當(dāng)前線程會交出處理器資源,而它們不同的是,sleep()交出來的時間片其他線程都可以去競爭,也就是說都有機(jī)會獲得當(dāng)前線程讓出的時間片。而yield()方法只允許與當(dāng)前線程具有相同優(yōu)先級的線程能夠獲得釋放出來的CPU時間片。



  4、守護(hù)線程Daemon


  守護(hù)線程是一種特殊的線程,就和它的名字一樣,它是系統(tǒng)的守護(hù)者,在后臺默默地守護(hù)一些系統(tǒng)服務(wù),比如垃圾回收線程,JIT線程就可以理解守護(hù)線程。與之對應(yīng)的就是用戶線程,用戶線程就可以認(rèn)為是系統(tǒng)的工作線程,它會完成整個系統(tǒng)的業(yè)務(wù)操作。用戶線程完全結(jié)束后就意味著整個系統(tǒng)的業(yè)務(wù)任務(wù)全部結(jié)束了,因此系統(tǒng)就沒有對象需要守護(hù)的了,守護(hù)線程自然而然就會退。當(dāng)一個Java應(yīng)用,只有守護(hù)線程的時候,虛擬機(jī)就會自然退出。下面以一個簡單的例子來表述Daemon線程的使用。


 public class DaemonDemo {

        public static void main(String[] args) {

            Thread daemonThread = new Thread(new Runnable() {

                @Override

                public void run() {

                    while (true) {

                        try {

                            System.out.println("i am alive");

                            Thread.sleep(500);

                        } catch (InterruptedException e) {

                            e.printStackTrace();

                        } finally {

                            System.out.println("finally block");

                        }

                    }

                }

            });

            daemonThread.setDaemon(true);

            daemonThread.start();

            //確保main線程結(jié)束前能給daemonThread能夠分到時間片

            try {

                Thread.sleep(800);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

    }


輸出結(jié)果為:


> i am alive finally block i am alive


上面的例子中daemodThread run方法中是一個while死循環(huán),會一直打印,但是當(dāng)main線程結(jié)束后daemonThread就會退出所以不會出現(xiàn)死循環(huán)的情況。main線程先睡眠800ms保證daemonThread能夠擁有一次時間片的機(jī)會,也就是說可以正常執(zhí)行一次打印“i am alive”操作和一次finally塊中"finally block"操作。緊接著main 線程結(jié)束后,daemonThread退出,這個時候只打印了"i am alive"并沒有打印finnal塊中的。因此,這里需要注意的是守護(hù)線程在退出的時候并不會執(zhí)行finnaly塊中的代碼,所以將釋放資源等操作不要放在finnaly塊中執(zhí)行,這種操作是不安全的。


  線程可以通過setDaemon(true)的方法將線程設(shè)置為守護(hù)線程。并且需要注意的是設(shè)置守護(hù)線程要先于start()方法,否則會報。


Exception in thread "main" java.lang.IllegalThreadStateException at java.lang.Thread.setDaemon(Thread.java:1365) at learn.DaemonDemo.main(DaemonDemo.java:19)


這樣的異常,但是該線程還是會執(zhí)行,只不過會當(dāng)做正常的用戶線程執(zhí)行。


以上就是動力節(jié)點java學(xué)院小編介紹的“java多線程的狀態(tài)轉(zhuǎn)換以及基本操作”的內(nèi)容,希望對大家有幫助,如有疑問,請在線咨詢,有專業(yè)老師隨時為你服務(wù)。


提交申請后,顧問老師會電話與您溝通安排學(xué)習(xí)

免費課程推薦 >>
技術(shù)文檔推薦 >>
主站蜘蛛池模板: 美女视频黄a视频全免费网站色窝 | 免费观看呢日本天堂视频 | 日韩在线观看你懂的 | 亚洲制服丝袜第一页 | 瑟瑟网站在线观看 | 一级毛片a女人刺激视频免费 | 性欧美4khdxxxx | 秋霞国产一级特黄 | 亚洲国产成人久久一区www妖精 | 国产中的精品一区的 | 国产综合亚洲专区在线 | 黄色视屏在线免费观看 | 国产精品毛片无码 | 久久亚洲国产高清 | 欧美日韩国产手机在线观看视频 | 激情综合网婷婷 | 一区二区三区福利视频 | 国产欧美日韩网站 | 亚洲国产一区二区三区 | 国产精品免费aⅴ片在线观看 | 成年视频在线观看免费 | 欧美一区二区三区免费 | 一级毛片看真人在线视频 | 天天射天天操天天干 | 久99久精品视频免费观看v | 美女一级毛片免费不卡视频 | 你懂得在线看 | 亚洲视频在线观看一区 | 中文字幕精品视频在线观 | 热99re久久精品香蕉 | 91精品国产闺蜜国产在线 | 1区1区3区4区产品亚洲 | 中国毛片基地 | 最近中文字幕无免费视频 | 青草视频国产 | 乱亲h女 小说 | 丁香伊人五月综合激激激 | 欧美日韩另类国产 | 在线日韩亚洲 | 日日摸碰夜夜爽 | 生活片毛片 |