可能是東半球最好的多執行緒講義!

melon_jj發表於2018-08-07

  JAVA多執行緒

  多執行緒的基本概念

  執行緒指程式中的一個執行場景,也就是執行流程,那麼程式和執行緒有什麼區別呢?

  • 每個程式是一個應用程式,都有獨立的記憶體空間

  • 同一個程式中的執行緒共享其程式中的記憶體和資源(共享的記憶體是堆記憶體和方法區記憶體,棧記憶體不共享,每個執行緒有自己的。)

  什麼是程式?

  一個程式對應一個應用程式。例如:在windows作業系統啟動Word就表示啟動了一個

  程式。在java的開發環境下啟動JVM,就表示啟動了一個程式。現代的計算機都是支援多

  程式的,在同一個作業系統中,可以同時啟動多個程式。

  多程式有什麼作用?

  單程式計算機只能做一件事情。

  玩電腦,一邊玩遊戲(遊戲程式)一邊聽音樂(音樂程式)。

  對於單核計算機來講,在同一個時間點上,遊戲程式和音樂程式是同時在執行嗎?不是。

  因為計算機的CPU只能在某個時間點上做一件事。由於計算機將在“遊戲程式”和“音樂

  程式”之間頻繁的切換執行,切換速度極高,人類感覺遊戲和音樂在同時進行。

  多程式的作用不是提高執行速度,而是提高CPU的使用率。

  程式和程式之間的記憶體是獨立的。

  什麼是執行緒?

  執行緒是一個程式中的執行場景。一個程式可以啟動多個執行緒。

  多執行緒有什麼作用?

  多執行緒不是為了提高執行速度,而是提高應用程式的使用率。

  執行緒和執行緒共享“堆記憶體和方法區記憶體”,棧記憶體是獨立的,一個執行緒一個棧。

  可以給現實世界中的人類一種錯覺:感覺多個執行緒在同時併發執行。

  java程式的執行原理?

  java命令會啟動java虛擬機器,啟動JVM,等於啟動了一個應用程式,表示啟動了一個程式。該程式會自動啟動一個“主執行緒”,然後主執行緒去呼叫某個類的main方法。所以main方法執行在主執行緒中。在此之前的所有程式都是單執行緒的。

  執行緒生命週期

  執行緒是一個程式中的執行場景,一個程式可以啟動多個執行緒

可能是東半球最好的多執行緒講義!
  新建:採用new語句建立完成

  就緒:執行start後

  執行:佔用CPU時間

  阻塞:執行了wait語句、執行了sleep語句和等待某個物件鎖,等待輸入的場合

  終止:退出run()方法

  多執行緒不是為了提高執行速度,而是提高應用程式的使用率.

  執行緒和執行緒共享”堆記憶體和方法區記憶體”.棧記憶體是獨立的,一個執行緒一個棧.

  可以給現實世界中的人類一種錯覺:感覺多執行緒在同時併發執行.

  很多人都對其中的一些概念不夠明確,如同步、併發等等,讓我們先建立一個資料字典,以免產生誤會。

  • 多執行緒:指的是這個程式(一個程式)執行時產生了不止一個執行緒

  • 並行與併發:

  •

  o 並行:多個cpu例項或者多臺機器同時執行一段處理邏輯,是真正的同時。

  •

  o 併發:通過cpu排程演算法,讓使用者看上去同時執行,實際上從cpu操作層面不是真正的同時。併發往往在場景中有公用的資源,那麼針對這個公用的資源往往產生瓶頸,我們會用TPS或者QPS來反應這個系統的處理能力。

  執行緒安全:經常用來描繪一段程式碼。指在併發的情況之下,該程式碼經過多執行緒使用,執行緒的排程順序不影響任何結果。這個時候使用多執行緒,我們只需要關注系統的記憶體,cpu是不是夠用即可。反過來,執行緒不安全就意味著執行緒的排程順序會影響最終結果,如不加事務的轉賬程式碼:

   voidtransferMoney(Userfrom,Userto,floatamount){

   to.setMoney(to.getBalance()+amount);

   from.setMoney(from.getBalance()-amount);

   }

  同步:Java中的同步指的是通過人為的控制和排程,保證共享資源的多執行緒訪問成為執行緒安全,來保證結果的準確。如上面的程式碼簡單加入@synchronized關鍵字。在保證結果準確的同時,提高效能,才是優秀的程式。執行緒安全的優先順序高於效能。

  Java命令會啟動Java虛擬機器,啟動JVM,等於啟動了一個應用程式,表示啟動了一個程式,該程式會自動啟動一個”主執行緒”,

  然後主執行緒去呼叫某個類的main()方法,所以main()方法執行在主執行緒中.

可能是東半球最好的多執行緒講義!
  執行緒的排程與控制

  執行緒的排程模型分為:分時排程模型和搶佔式排程模型,Java使用搶佔式排程模型

  通常我們的計算機只有一個CPU,CPU在某一個時刻只能執行一條指令,執行緒只有得到CPU時間片,也就是使用權,才可以執行指令。在單CPU的機器上執行緒不是並行執行的,只有在多個CPU上執行緒才可以並行執行。Java虛擬機器要負責執行緒的排程,取得CPU的使用權,目前有兩種排程模型:分時排程模型和搶佔式排程模型,Java使用搶佔式排程模型。分時排程模型:所有執行緒輪流使用CPU的使用權,平均分配每個執行緒佔用CPU的時間片搶佔式排程模型:優先讓優先順序高的執行緒使用CPU,如果執行緒的優先順序相同,那麼會隨機選擇一個,優先順序高的執行緒獲取的CPU時間片相對多一些。

   分時排程模型:所有執行緒輪流使用CPU的使用權,平均分配每個執行緒佔用CPU的時間片

   搶佔式排程模型:優先讓優先順序高的執行緒使用CPU,如果執行緒的優先順序相同,那麼會隨機選擇一個,優先順序高的執行緒獲取的CPU時間片相對多一些.

  publicclassThreadTest{

   publicstaticvoidmain(String[]args){

   ThreadTest1();

   //ThreadTest2();

   //ThreadTest3();

   //ThreadTest4();

   //ThreadTest5();

   }

   /**

   *三個方法:獲取當前執行緒物件:Thread.currentThread();給執行緒起名:t1.setName("t1");獲取執行緒的名字:

   *t.getName();

   */

   privatestaticvoidThreadTest1(){

   Threadt=Thread.currentThread();//t儲存的記憶體地址指向的執行緒為"主執行緒"

   System.out.println(t.getId());

   Threadt1=newThread(newProcessor1());

   //給執行緒起名

   t1.setName("t1");

   t1.start();

   Threadt2=newThread(newProcessor1());

   t2.setName("t2");

   t2.start();

   }

   /**

   *執行緒優先順序高的獲取的CPU時間片相對多一些優先順序:1-10最低:1最高:10預設:5

   */

   privatestaticvoidThreadTest2(){

   Threadt1=newProcessor2();

   Threadt2=newProcessor2();

   t1.setName("t1");

   t2.setName("t2");

   System.out.println(t1.getPriority());

   System.out.println(t2.getPriority());

   t1.setPriority(1);

   t2.setPriority(10);

   t1.start();

   t2.start();

   }

   /**

   *1.Thread.sleep(毫秒);2.sleep方法是一個靜態方法3.該方法的作用:阻塞當前執行緒,騰出CPU,讓給其它執行緒

   */

   privatestaticvoidThreadTest3(){

   Threadt=newThread(newProcessor3());

   t.start();

   for(inti=0;i<11;i++){

   System.out.println(Thread.currentThread().getName()+"========>"

   +i);

   try{

   t.sleep(5000);//等同於Thread.sleep(5000);阻塞的還是當前執行緒,和t執行緒無關.

   }catch(InterruptedExceptione){

   e.printStackTrace();

   }

   }

   }

   /**

   *某執行緒正在休眠,如何打斷它的休眠以下方式依靠的是異常處理機制

   */

   privatestaticvoidThreadTest4(){

   try{

   Threadt=newThread(newProcessor4());

   t.start();

   Thread.sleep(5000);//睡5s

   t.interrupt();//打斷Thread的睡眠

   }catch(InterruptedExceptione){

   e.printStackTrace();

   }

   }

   /**

   *如何正確的更好的終止一個正在執行的執行緒需求:執行緒啟動5s之後終止.

   */

   privatestaticvoidThreadTest5(){

   Processor5p=newProcessor5();

   Threadt=newThread(p);

   t.start();

   //5s之後終止

   try{

   Thread.sleep(5000);

   p.isRun=false;

   }catch(InterruptedExceptione){

   //TODOAuto-generatedcatchblock

   e.printStackTrace();

   }

   }

  }

  classProcessor1implementsRunnable{

   @Override

   publicvoidrun(){

   Threadt=Thread.currentThread();//t儲存的記憶體地址指向的執行緒為"t1執行緒物件"

   System.out.println(t.getName());

   System.out.println(t.getId());

   }

  }

  classProcessor2extendsThread{

   @Override

   publicvoidrun(){

   for(inti=0;i<50;i++){

   System.out.println(Thread.currentThread().getName()

   +"----------->"+i);

   }

   }

  }

  classProcessor3implementsRunnable{

   /**

   *Thread中的run方法不能丟擲異常,所以重寫runn方法之後,在run方法的宣告位置上不能使用throws

   *所以run方法中的異常只能try...catch...

   */

   @Override

   publicvoidrun(){

   for(inti=0;i<11;i++){

   System.out.println(Thread.currentThread().getName()+"========>"

   +i);

   try{

   Thread.sleep(1000);

   }catch(InterruptedExceptione){

   e.printStackTrace();

   }

   }

   }

  }

  classProcessor4implementsRunnable{

   @Override

   publicvoidrun(){

   try{

   Thread.sleep(1000000000);

   System.out.println("能否執行這裡");

   }catch(InterruptedExceptione){

   e.printStackTrace();

   }

   for(inti=0;i<11;i++){

   System.out.println(Thread.currentThread().getName()+"========>"

   +i);

   }

   }

  }

  classProcessor5implementsRunnable{

   booleanisRun=true;

   @Override

   publicvoidrun(){

   for(inti=0;i<11;i++){

   if(isRun){

   try{

   Thread.sleep(1000);

   }catch(InterruptedExceptione){

   e.printStackTrace();

   }

   System.out.println(Thread.currentThread().getName()

   +"========>"+i);

   }

   }

   }

  }

  執行緒優先順序

  執行緒優先順序主要分三種:MAX_PRIORITY(最高);MIN_PRIORITY(最低階)NORM_PRIORITY(標準)預設

  //設定執行緒的優先順序,執行緒啟動後不能再次設定優先順序

  //必須在啟動前設定優先順序

  //設定最高優先順序

  t1.setPriority(Thread.MAX_PRIORITY);

  sleep

  publicclassSleepTest{

   publicstaticvoidmain(String[]args){

   System.out.println("Wait");

   //讓主執行緒等待5秒再執行

   Wait.bySec(5);

   //提示恢復執行

   System.out.println("start");

   }

  }

  classWait{

   publicstaticvoidbySec(longs){

   //sleeps個1秒

   for(inti=0;i<s;i++){

   System.out.println(i+1+"秒");

   try{

   //sleep1秒

   Thread.sleep(1000);

   }catch(InterruptedExceptione){

   e.printStackTrace();

   }

   }

   }

  }

  停止一個執行緒

   如果我們的執行緒正在睡眠,可以採用interrupt進行中斷

   通常定義一個標記,來判斷標記的狀態停止執行緒的執行

  yield

  它與sleep()類似,只是不能由使用者指定暫停多長時間,並且yield()方法只能讓同優先順序的執行緒有執行的機會,採用yieid可以將CPU的使用權讓給同一個優先順序的執行緒

  publicclassYieldTest{

   publicstaticvoidmain(String[]args){

   FirstThreadmt=newFirstThread();

   SecThreadmnt=newSecThread();

   mt.start();

   mnt.start();

   }

  }

  classFirstThreadextendsThread{

   publicvoidrun(){

   for(inti=0;i<5;i++){

   System.out.println("第一個執行緒的第"+(i+1)+"次執行");

   Thread.yield();//暫停執行緒

   }

   }

  }

  classSecThreadextendsThread{

  publicvoidrun(){

   for(inti=0;i<5;i++){

   System.out.println("第二個執行緒的第"+(i+1)+"次執行");

   Thread.yield();

   }

   }

  }

  join

  當前執行緒可以呼叫另一個執行緒的join方法,呼叫後當前執行緒會被阻塞不再執行,直到被呼叫的執行緒執行完畢,當前執行緒才會執行

  publicclassJoinTestextendsThread{

   publicJoinTest(Stringname){

   super(name);

   }

   publicvoidrun(){

   for(inti=0;i<5;i++)

   System.out.println(getName()+""+i);

   }

   publicstaticvoidmain(String[]args){

   for(inti=0;i<10;i++){

   if(i==5){

   JoinTesttempjt=newJoinTest("半路加入的執行緒");

   try{

   tempjt.start();

   tempjt.join();

   }catch(InterruptedExceptione){

   e.printStackTrace();

   }

   }

   System.out.println(Thread.currentThread().getName()+""+i);

   }

   }

  }

  synchronized

  執行緒同步,指某一個時刻,指允許一個執行緒來訪問共享資源,執行緒同步其實是對物件加鎖,如果物件中的方法都是同步方法,那麼某一時刻只能執行一個方法,採用執行緒同步解決以上的問題,我們只要保證執行緒一操作s時,執行緒2不允許操作即可,只有執行緒一使用完成s後,再讓執行緒二來使用s變數

  • 非同步程式設計模型:t1執行緒執行t1的,t2執行緒執行t2的,兩個執行緒之間誰也不等誰.

  • 同步程式設計模型:t1執行緒和t2執行緒執行,t2執行緒必須等t1執行緒執行結束之後,t2執行緒才能執行,這是同步程式設計模型.

  •

  • 什麼時候要用同步呢?為什麼要引入執行緒同步呢?

  • 1.為了資料的安全,儘管應用程式的使用率降低,但是為了保證資料是安全的,必須加入執行緒同步機制.

  • 執行緒同步機制使程式變成了(等同)單執行緒.

  • 2.什麼條件下要使用執行緒同步?

  • 第一:必須是多執行緒環境

  • 第二:多執行緒環境共享同一個資料.

  • 第三:共享的資料涉及到修改操作.

   //synchronized是對物件加鎖

   //採用synchronized同步最好只同步有執行緒安全的程式碼

   //可以優先考慮使用synchronized同步塊

   //因為同步的程式碼越多,執行的時間就會越長,其他執行緒等待的時間就會越長

   //影響效率

  publicclassTestWithdrawal{

   publicstaticvoidmain(String[]args){

   //建立兩個執行緒

   TestAccountr=newTestAccount();

   Threadone=newThread(r);

   Threadtwo=newThread(r);

   one.setName("張三");

   two.setName("張三的妻子");

   //啟動執行緒

   one.start();

   two.start();

   }

  }

  classAccount{

   privateintbalance=500;//餘額

   publicintgetBalance(){

   returnbalance;

   }

   //取款

   publicvoidwithdraw(intamount){

   balance=balance-amount;

   }

  }

  classTestAccountimplementsRunnable{

   //所有用TestAccount物件建立的執行緒共享同一個帳戶物件

   privateAccountacct=newAccount();

   publicvoidrun(){

   for(inti=0;i<5;i++){

   makeWithdrawal(100);//取款

   if(acct.getBalance()<0){

   System.out.println("賬戶透支了!");

   }

   }

   }

   privatevoidmakeWithdrawal(intamt){

   synchronized(acct){

   if(acct.getBalance()>=amt){

   System.out.println(Thread.currentThread().getName()+"準備取款");

   try{

   Thread.sleep(500);//0.5秒後實現取款

   }catch(InterruptedExceptionex){

   }

   //如果餘額足夠,則取款

   acct.withdraw(amt);

   System.out.println(Thread.currentThread().getName()+"完成取款,餘額:"+acct.getBalance());

   }else{

   //餘額不足給出提示

   System.out.println("餘額不足以支付"

   +Thread.currentThread().getName()+"的取款,餘額為"

   +acct.getBalance());

   }

   }

   }

  }

  死鎖

  publicclassDeadLock{

   publicstaticvoidmain(String[]args){

   Objecto1=newObject();

   Objecto2=newObject();

   Threadt1=newThread(newT1(o1,o2));

   Threadt2=newThread(newT2(o1,o2));

   t1.start();

   t2.start();

   }

  }

  classT1implementsRunnable{

   Objecto1;

   Objecto2;

   T1(Objecto1,Objecto2){

   this.o1=o1;

   this.o2=o2;

   }

   @Override

   publicvoidrun(){

   synchronized(o1){

   try{

   Thread.sleep(1000);

   }catch(InterruptedExceptione){

   e.printStackTrace();

   }

   synchronized(o2){

   }

   }

   }

  }

  classT2implementsRunnable{

   Objecto1;

   Objecto2;

   T2(Objecto1,Objecto2){

   this.o1=o1;

   this.o2=o2;

   }

   @Override

   publicvoidrun(){

   synchronized(o2){

   try{

   Thread.sleep(1000);

   }catch(InterruptedExceptione){

   e.printStackTrace();

   }

   synchronized(o1){

   }

   }

   }

  }

  守護執行緒

  從執行緒分類上可以分為:使用者執行緒(以上講的都是使用者執行緒),另一個是守護執行緒。守護執行緒是這樣的,所有的使用者執行緒結束生命週期,守護執行緒才會結束生命週期,只要有一個使用者執行緒存在,那麼守護執行緒就不會結束,例如java中著名的垃圾回收器就是一個守護執行緒,只有應用程式中所有的執行緒結束,它才會結束。

  • 其它所有的使用者執行緒結束,則守護執行緒退出!

  • 守護執行緒一般都是無限執行的.

  publicclassDaemonThread{

   publicstaticvoidmain(String[]args)throwsInterruptedException{

   Threadt1=newThread(newRunnable2());

   t1.setName("t1");

   //將t1這個使用者執行緒修改成守護執行緒.線上程沒有啟動時可以修改以下引數

   t1.setDaemon(true);

   t1.start();

   //主執行緒

   for(inti=0;i<10;i++){

   System.out.println(Thread.currentThread().getName()+"----->"+i);

   Thread.sleep(1000);

   }

   }

  }

  classRunnable2implementsRunnable{

   @Override

   publicvoidrun(){

   inti=0;

   while(true){

   i++;

   System.out.println(Thread.currentThread().getName()+"-------->"

   +i);

   try{

   Thread.sleep(500);

   }catch(InterruptedExceptione){

   e.printStackTrace();

   }

   }

   }

  }

  設定為守護執行緒後,當主執行緒結束後,守護執行緒並沒有把所有的資料輸出完就結束了,也即是說守護執行緒是為使用者執行緒服務的,當使用者執行緒全部結束,守護執行緒會自動結束

  Timer.schedule()

  /**

  *關於定時器的應用作用:每隔一段固定的時間執行一段程式碼

  */

  publicclassTimerTest{

   publicstaticvoidmain(String[]args)throwsParseException{

   //1.建立定時器

   Timert=newTimer();

   //2.指定定時任務

   t.schedule(newLogTimerTask(),newSimpleDateFormat(

   "yyyy-MM-ddHH:mm:ssSSS").parse("2017-06-2914:24:00000"),

   10*1000);

   }

  }

  //指定任務

  classLogTimerTaskextendsTimerTask{

   @Override

   publicvoidrun(){

   System.out.println(newSimpleDateFormat("yyyy-MM-ddHH:mm:ssSSS")

   .format(newDate()));

   }

  }

   我是melon,一個10年程式設計老司機。Q我3474203856。 給melon留言或者說明是看到文章過來的。所有相關技術問題都可以一起尋找到答案。

相關文章