跟着剛哥梳理java知識點——多線程(十六)

建立多線程
第一種方式
① 繼承:繼承Thread。
② 重寫:重寫Thread類的run()方法
③ 建立:建立一個子類的對象
④ 調用:調用線程的start()方法,啓動此線程,調用run()方法html

 1 class Work extends Thread{ //① 繼承
 2   @Override
 3   //② 重寫
 4   public void run() {
 5     for (int i = 1 ;i < 5; i++) {
 6       System.out.println(Thread.currentThread().getName()+":"+i);
 7     }
 8   }
 9 }
10 public static void main(String[] args) {
11   //③ 建立
12   Work work = new Work();
13   //④ 調用
14   work.start();
15   for (int i = 1 ;i < 5; i++) {
16     System.out.println(Thread.currentThread().getName()+":"+i);
17   }
18 }

輸出結果:java

main:1
main:2
main:3
main:4
Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4

思考:把上面的start修改爲run,想一想會有什麼結果呢?mysql

main:1
main:2
main:3
main:4
main:1
main:2
main:3
main:4

走了兩遍的main。由於Start是啓動線程,run只是正常的調用了一下方法,和多線程不要緊。
面試


第二種方法
① 實現接口:實現Runnable接口的類
② 實現抽象方法:實現接口的run的方法
③ 建立對象:建立一個Runnable接口實現類的對象
④ 放入構造器:將此對象做爲形參傳遞給Thread的構造器,建立Thread對象
⑤ 啓動線程:啓動這個線程sql

 1 class Work implements Runnable{ //① 實現接口
 2   //② 實現抽象方法
 3   public void run() {
 4     for (int i = 11 ;i < 100; i++) {
 5       System.out.println(Thread.currentThread().getName()+":"+i);
 6     }
 7   }
 8 }
 9 
10 public static void main(String[] args) {
11   //③ 建立對象
12   Work work = new Work();
13   //④ 放入構造器
14   Thread t1 = new Thread(work);
15   //⑤ 啓動線程
16   t1.start();
17   for (int i = 11 ;i < 100; i++) {
18     System.out.println(Thread.currentThread().getName()+":"+i);
19   }
20 }

 

Thread(類) VS Runnable(接口)
① Runnable避免了java類的單繼承侷限性,接口能夠多繼承。
② 若是多個線程操做同一份資源更適合使用Runnable的方式數據庫


線程Thread的經常使用方法:
① start():啓動線程並執行相應的run()方法
② run():將子線程要執行的代碼放入run()方法
③ currentThread():靜態的,調取當前的線程
    √ getName():獲取此線程的名字
   例如:Thread.currentThread().getName()編程

    √ setName():設置線程的名字緩存

④ yield():強制釋放當前cpu執行權,(例如子線程和主線程都循環輸出100次的數字,當主線程%10==0的時候,就調用主線程yield方法Thread.currentThread().yield(),強制主線程釋放CPU執行權)須要說明的是釋放線程的CPU執行權不表明其餘線程就必定能搶到CPU的執行權。也可能釋放的線程再次搶到資源。
⑤ join():在A線程中調用B線程join(參與進來的意思)方法,表示當執行到此方法,A線程中止執行,B執行完畢後,A再執行。
⑥ sleep():顯式的讓當前線程睡眠1毫秒安全

設置線程的優先級:優先級高只能說明搶到的概率高,不表明必定先完成
① getPriority():獲取線程的優先級
② setPriority():設置線程的優先級
一共是10個等級.默認是等級5,Thread裏的屬性就是等級級別
Thread屬性:
√ MAX_PRIORITY:最高的線程優先級
√ MIN_PRIORITY:最低的線程優先級
√ NORM_PRIORITY:默認的線程優先級
線程分爲兩類
①守護線程
   用來服務用戶的,垃圾回收就是一個典型的守護線程
   若JVM都是守護線程,當前JVM將退出
②用戶線程
   用戶本身建立的線程
   用戶線程-->守護線程: 
   經過在start()方法前調用thread.setDaemon(True)就能夠多線程

線程的生命週期:枚舉status表明了狀態

一、新建狀態(New):新建立了一個線程對象。
二、就緒狀態(Runnable):線程對象建立後,其餘線程調用了該對象的start()方法。該狀態的線程位於可運行線程池中,變得可運行,等待獲取CPU的使用權。
三、運行狀態(Running):就緒狀態的線程獲取了CPU,執行程序代碼。
四、阻塞狀態(Blocked):阻塞狀態是線程由於某種緣由放棄CPU使用權,暫時中止運行。直到線程進入就緒狀態,纔有機會轉到運行狀態。阻塞的狀況分三種:
   (一)、等待阻塞:運行的線程執行wait()方法,JVM會把該線程放入等待池中。
   (二)、同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入鎖池中。
   (三)、其餘阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程從新轉入就緒狀態。
五、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。

線程安全性
緣由:
因爲一個線程在操做共享數據過程當中未執行完的狀況下,另外的線程參與進來,
致使共享數據存在安全問題。
共享數據:多個線程共共同操做同一個數據(變量)

解決:
必須讓一個線程操做共享數據完畢以後,其餘線程纔有機會共享數據的操做

java如何解決的呢?
方式一:同步代碼塊
synchronized(同步監視器){
//須要同步的代碼

}

同步監視器知識點:對於通常的方法,同步監視器能夠考慮使用this關鍵字。synchronized(this)
                  對於靜態方法而言,同步監視器使用當前類自己充當鎖。synchronized(Singleton.class)

同步塊包住誰呢?誰操做共享數據就包誰
注意:在實現的方式中,可使用this充當鎖,可是在繼承的方式中,慎用this

單例模式:線程安全

 1 class Singleton{
 2     private Singleton(){
 3     
 4     }
 5     private static volatile Singleton instance = null;
 6     public static Singleton getInstance(){
 7     if(instance == null){
 8       synchronize(Singleton.calss){
 9         if(instance == null){
10          instance = new Singleton();
11        }
12      }
13    }
14    return instance;
15 }

方式二:同步方法
同步方法的鎖:this

public synchronize void 方法(){

}

面試題:
銀行有一個帳號,有兩個儲戶分別向一個帳戶存入3000元,每次存1000,存3次
,每次存完後打印帳戶餘額

分析:
共享資源是什麼?顯然是一個帳號。
是否須要用多線程?顯然是用的,由於有兩個儲戶

 1 class Account {
 2   double balance = 0;
 3   public synchronized void cunqian(double crm){
 4     balance += crm;
 5     System.out.println(Thread.currentThread().getName() + ":" + balance);
 6   }
 7 }
 8 class Customer implements Runnable{
 9   Account account;
10   public Customer(Account account) {
11     this.account = account;
12   }
13   @Override
14   public void run() {
15     for (int i = 0; i < 3; i++) {
16       account.cunqian(1000);
17     }
18   }
19 }
20 public static void main(String[] args) throws Exception {
21   Account account = new Account();
22   Customer customer = new Customer(account);
23   Thread t1 = new Thread(customer);
24   t1.start();
25   Thread t2 = new Thread(customer);
26   t2.start();
27   t1.setName("儲戶1");
28   t2.setName("儲戶2");
29 }

上面代碼須要須要的是:因爲是兩個類,必定要保證共享資源類千萬不要被屢次實例化
因此必定要讓第一個類實例化完成後當成形參出入到第二個中構造(看紅色標記部分)


線程通訊:(三個關鍵字使用的話,都必須在同步代碼塊或同步方法中,三個方法之因此不放在上面的Thread類的方法裏,是由於這三個方法是在object裏的方法
① wait():令當前線程掛起並放棄CPU、同步資源。讓別的線程可訪問並修改共享資源,而當前前程排隊等候再次對資源的訪問
② notify():喚醒正在排隊等候同步資源的線程中優先級最高的鎖
③ notifyAll():喚醒全部正在排隊的等待的全部線程結束等待

 

面試題:

一、什麼是線程?線程和進程的區別?
  進程:是是程序的一次動態執行,它經歷了從代碼加載,執行,到執行完畢的一個完整過程。這個過程也是進程自己從產生、發展,到最終消亡的一個的生命週期。

      線程:能夠理解爲進程的多條執行線索,每條線索又對應着各自獨立的生命週期。線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。一個線程能夠建立和撤銷另外一個線程,同一個進程中的多個線程之間能夠併發執行。

二、用戶線程和守護線程的區別?

  用戶線程和守護線程都是線程,區別是Java虛擬機在全部用戶線程dead後,程序就會結束。而無論是否還有守護線程還在運行,若守護線程還在運行,則會立刻結束。很好理解,守護線程是用來輔助用戶線程的,如公司的保安 和員工,各司其職,當員工都離開後,保安天然下班了。

三、建立線程有哪幾種方式?

  這裏就不說了,上面已經梳理了。

四、能夠直接調用Thread類裏的run()方法嗎?

  固然能夠調用,就是不會啓動線程。

五、你如何理解多線程的優先級的?

  在操做系統中,線程能夠劃分優先級,優先級較高的線程獲得CPU資源較多,也就是CPU優先執行優先級較高的線程對象中的任務(其實並非這樣)。

在Java中,線程的優先級用setPriority()方法就行,線程的優先級分爲1-10這10個等級,若是小於1或大於10,則拋出異常throw new IllegalArgumentException(),默認是5。

線程的優先級仍然沒法保障線程的執行次序。只不過,優先級高的線程獲取CPU資源的機率較大,優先級低的並不是沒機會執行。

六、線程之間如何同步的?

  Java學習筆記---多線程同步的五種方法

七、如何確保線程安全?如何在兩個線程之間共享數據?

  JAVA 併發編程-多個線程之間共享數據(六)

八、volatile關鍵字在Java中有什麼做用?

  java中volatile關鍵字的含義

九、同步方法和同步塊哪一個更好?

  同步是一個高開銷的操做,所以應該儘可能減小同步的內容,故同步塊是更好的選擇,由於它不會鎖住整個對象。同步方法會鎖住整個對象,這一般會致使他們中止執行並須要等待得到這個對象上的鎖。

十、什麼是死鎖?如何避免?

  死鎖:若是線程1鎖住了A,而後嘗試對B進行加鎖,同時線程2已經鎖住了B,接着嘗試對A進行加鎖,這時死鎖就發生了。

  如何避免:

  ① 加鎖順序:若是能確保全部的線程都是按照相同的順序得到鎖,那麼死鎖就不會發生

  ② 加鎖時限:在嘗試獲取鎖的時候加一個超時時間,這也就意味着在嘗試獲取鎖的過程當中若超過了這個時限該線程則放棄對該鎖請求

  ③ 死鎖檢測:當獲取鎖的時候作好記錄,釋放鎖的時候作好記錄,一旦檢測到有死鎖發生的時候,就要對進行回退操做。

十一、什麼是樂觀鎖和悲觀鎖?

  悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿數據的時候都認爲別人會修改,因此每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關係型數據庫裏邊就用到了不少這種鎖機制,好比行鎖,表鎖等,讀鎖,寫鎖等,都是在作操做以前先上鎖。

  樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數據的時候都認爲別人不會修改,因此不會上鎖,可是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可使用版本號等機制。樂觀鎖適用於多讀的應用類型,這樣能夠提升吞吐量,像數據庫若是提供相似於write_condition機制的其實都是提供的樂觀鎖。

   兩種鎖各有優缺點,不可認爲一種好於另外一種,像樂觀鎖適用於寫比較少的狀況下,即衝突真的不多發生的時候,這樣能夠省去了鎖的開銷,加大了系統的整個吞吐量。但若是常常產生衝突,上層應用會不斷的進行retry,這樣反卻是下降了性能,因此這種狀況下用悲觀鎖就比較合適。

十二、什麼是線程池?爲何要使用它?

  建立線程要花費昂貴的資源和時間,若是任務來了才建立線程那麼響應時間會變長,並且一個進程能建立的線程數有限。爲了不這些問題,在程序啓動的時候就建立若干線程來響應處理,它們被稱爲線程池,裏面的線程叫工做線程。從JDK1.5開始,Java API提供了Executor框架讓你能夠建立不一樣的線程池。好比單線程池,每次處理一個任務;數目固定的線程池或者是緩存線程池(一個適合不少生存期短的任務的程序的可擴展線程池)。

1三、Java Concurrency API中的Lock接口(Lock interface)是什麼?對比同步它有什麼優點?

  Java中的鎖-Lock接口解析

1四、什麼是阻塞隊列?如何使用阻塞隊列來實現生產者-消費者模型?

  JAVA中的阻塞隊列

1五、多線程中棧與堆是公有的仍是私有的?

  棧私有,堆公有

1六、寫一個線程安全的單例模式?雙重鎖是作什麼用的?

 1  public class Singleton {  
 2        private static volatile Singleton instance;  
 3        private Singleton (){
 4        }   
 5        public static Singleton getInstance(){    //對獲取實例的方法進行同步
 6          if (instance == null){
 7              synchronized(Singleton.class){
 8                  if (instance == null)
 9                      instance = new Singleton(); 
10             }
11         }
12         return instance;
13       }
14       
15   }

1七、有1個全局變量,int sum=0,運行2個線程,代碼以下
     for(int i=1;i<=50;i++){

    sum=sum+1;

   }
       問2個線程都運行完以後,sum的取值範圍。

 1 public class Main implements Runnable{
 2     private int sum = 0;
 3     @Override
 4     public void run() {
 5         for (int i = 1 ;i <= 50; i++) {
 6             sum = +1;
 7         }
 8         System.out.println(sum);
 9     }
10     public static void main(String[] args) {
11         Main main = new Main();
12         Thread thread1 = new Thread(main);
13         Thread thread2 = new Thread(main);
14         thread1.start();
15         thread2.start();
16     }
17 }

  取值範圍:1-100

1八、有三個線程T1,T2,T3,怎麼確保它們按順序執行

  使用join方法便可。代碼: 有三個線程T1 T2 T3,如何保證他們按順序執行

1九、編寫一個程序,開啓3個線程,這3個線程的ID分別爲A、B、C,每一個線程將本身的ID在屏幕上打印10遍,要求輸出結果必須按ABC的順序顯示;如:ABCABC….依次遞推。

  http://www.cnblogs.com/baizhanshi/p/6428810.html

20、子線程循環 10 次,接着主線程循環 20次,接着又回到子線程循環 10 次,接着再回到主線程又循環 20 次,如此循環50次,試寫出代碼。

 1 package com.wang.reflect;
 2 //編寫功能類,實現子線程和主線程的功能
 3 class Function{
 4     private boolean flag=false;
 5     //子線程要實現的功能
 6     public synchronized void sub(){
 7         while(flag){
 8             try {
 9                 this.wait();
10             } catch (InterruptedException e) {
11                 e.printStackTrace();
12             }
13         }
14                
15         for(int i=0;i<10;i++){
16             //for循環內定義子線程的功能,這裏簡單的假設爲打印一句話,主線程同理
17             System.out.println("sub"+i);
18         }
19         
20         flag=true;
21         this.notify();
22     }
23     //主線程要實現的功能
24     public synchronized void main(){
25         while(!flag){
26             try {
27                 this.wait();
28             } catch (InterruptedException e) {
29                 e.printStackTrace();
30             }
31         }
32         for(int i=0;i<20;i++){
33             System.out.println("main"+i);
34         }
35         
36         flag=false;
37         this.notify();
38     }
39     
40 }
41 
42 public class Demo01 {
43   
44     public static void main(String[] args) {
45          final Function f=new Function();
46         new Thread(
47                 new Runnable(){
48 
49                     @Override
50                     public void run() {
51                         for(int i=0;i<50;i++){
52                             f.sub();
53                         }
54                     }
55                 
56                 }
57                 ).start();
58         
59         for(int i=0;i<50;i++){
60             f.main();
61         }
62     }
63 }

JDK1.5之後,出現了Lock和condition,Lock相似於synchronized功能,用來進行線程同步,Condition功能相似於Object類中的wait和notify方法,用於線程間的通訊.上面的代碼能夠用Lock和Condition來改進,以下:

 1 package com.wang.reflect;
 2 
 3 import java.util.concurrent.locks.Condition;
 4 import java.util.concurrent.locks.Lock;
 5 import java.util.concurrent.locks.ReentrantLock;
 6 
 7 //編寫功能類,實現子線程和主線程的功能
 8 class Function{
 9     private boolean flag=false;
10     
11     Lock lock=new ReentrantLock();
12     Condition con=lock.newCondition();
13     //子線程要實現的功能
14     public  void sub(){
15         lock.lock();
16         try {
17             
18             while(flag){
19                 try {
20                     con.await();
21                 } catch (InterruptedException e) {
22                     e.printStackTrace();
23                 }
24             }
25                        
27             for(int i=0;i<10;i++){
28                 //for循環內定義子線程的功能,這裏簡單的假設爲打印一句話,主線程同理
29                 System.out.println("sub"+i);
30             }
31             
32             flag=true;
33             con.signal();
34         } finally{
35             lock.unlock();
36         }
37     }
38     //主線程要實現的功能
39     public synchronized void main(){
40         lock.lock();
41         try {
42             while (!flag) {
43                 try {
44                     con.await();
45                 } catch (InterruptedException e) {
46                     e.printStackTrace();
47                 }
48             }
49             for (int i = 0; i < 20; i++) {
50                 System.out.println("main" + i);
51             }
52             flag = false;
53             con.signal();
54         } finally{
55             lock.unlock();
56         }
57     }
59 }
60 
62 public class Demo01 {
66     public static void main(String[] args) {
67          final Function f=new Function();
68         new Thread(
69                 new Runnable(){
70 
71                     @Override
72                     public void run() {
73                         for(int i=0;i<50;i++){
74                             f.sub();
75                         }
76                     }
77                 
78                 }
79                 ).start();
80         
81         for(int i=0;i<50;i++){
82             f.main();
83         }
84     }
85 }
相關文章
相關標籤/搜索