1. 程式人生 > >【Java基礎總結】多線程

【Java基礎總結】多線程

none 加載 引用 rup 什麽 true 執行過程 lose 好處

1. java中實現多線程的兩種方式

 1 //第一種:繼承Thread類,重寫run()方法
 2 class ThreadTest1 extends Thread{
 3     public void run(){
 4         String threadName = Thread.currentThread().getName();
 5         for(int i=0;i<10;i++){
 6             System.out.println("ThreadTest1 "+threadName+" running ... "+i);
 7
} 8 } 9 } 10 11 //第二種:實現Runnable接口,重寫run()方法 12 class ThreadTest2 implements Runnable{ 13 public void run(){ 14 String threadName = Thread.currentThread().getName(); 15 for(int i=0;i<10;i++){ 16 System.out.println("ThreadTest2 "+threadName+" running ... "+i);
17 } 18 } 19 } 20 21 public class Thread01{ 22 public static void main(String[] args){ 23 ThreadTest1 t1 = new ThreadTest1(); 24 ThreadTest1 t2 = new ThreadTest1(); 25 Thread t3 = new Thread(new ThreadTest2());  //註意 26 Thread t4 = new Thread(new
ThreadTest2()); 27 28 //start() 啟動線程 29 t1.start(); 30 t2.start(); 31 t3.start(); 32 t4.start(); 33 } 34 }

歸根結底,兩種方法都是用Thread類或其子類對象調用start()方法啟動線程。

為什麽用start()方法而不用run()方法啟動線程呢?因為start()方法產生了運行這個線程所需的系統資源,安排其運行,並調用線程體(run()方法)。

一個線程只能啟動一次,不管調用多少次start()方法,結果也只用一個線程。

2. 線程共享資源

建議實現Runnable接口,其好處是:

  • 多線程之間可以共享資源
  • 避免單繼承帶來的問題
  • 數據和代碼獨立
 1 class ThreadTest2 implements Runnable{
 2     private int count=20;    //共享資源
 3     public void run(){
 4         String threadName = Thread.currentThread().getName();
 5         while(count>0){
 6             System.out.println("ThreadTest2 "+threadName+" 售出 "+count+" 號票");
 7             count--;
 8         }
 9     }
10 }
11 
12 public class ThreadDemo2{
13     public static void main(String[] args){
14         ThreadTest2 t2 = new ThreadTest2();
15         (new Thread(t2,"窗口1")).start();
16         (new Thread(t2,"窗口2")).start();
17         (new Thread(t2,"窗口3")).start();
18         (new Thread(t2,"窗口4")).start();    
19     }
20 }

運行結果:

ThreadTest2 窗口1 售出 20 號票
ThreadTest2 窗口1 售出 19 號票
ThreadTest2 窗口3 售出 20 號票
ThreadTest2 窗口2 售出 20 號票
ThreadTest2 窗口3 售出 17 號票
ThreadTest2 窗口1 售出 18 號票
ThreadTest2 窗口1 售出 14 號票
ThreadTest2 窗口4 售出 19 號票
ThreadTest2 窗口1 售出 13 號票
ThreadTest2 窗口3 售出 15 號票
ThreadTest2 窗口3 售出 10 號票
ThreadTest2 窗口2 售出 16 號票
ThreadTest2 窗口2 售出 8 號票
ThreadTest2 窗口3 售出 9 號票
ThreadTest2 窗口1 售出 11 號票
ThreadTest2 窗口4 售出 12 號票
ThreadTest2 窗口1 售出 5 號票
ThreadTest2 窗口3 售出 6 號票
ThreadTest2 窗口2 售出 7 號票
ThreadTest2 窗口3 售出 2 號票
ThreadTest2 窗口1 售出 3 號票
ThreadTest2 窗口4 售出 4 號票
ThreadTest2 窗口2 售出 1 號票

問題:多個窗口售出了相同編號的票,發生了訪問沖突。

3. 線程同步

同步synchronized意:協同步調,按照先後順序進行;同步代碼塊;同步函數

前提:2個或2個以上的線程;使用同一把鎖
作用:保證同步中只有一個線程在運行。
好處:解決線程安全問題
弊端:多個線程需要判斷鎖,消耗資源

在Java裏面,同步鎖的概念就是這樣的。任何一個Object Reference都可以作為同步鎖。我們可以把Object Reference理解為對象在內存分配系統中的內存地址。
    
解決方案:
1)同步代碼塊
    synchronized(類或對象)
    {
        需要同步的代碼段
    }
2)同步函數
    (非static的情況)
    public synchronized void fun()
    {
        代碼段
    }
    等價於
    public void fun()
    {
        synchronized(this)
        {
            代碼段
        }
    }
    調用此同步函數的對象作為此同步函數的同步鎖。
    
    (static的情況)
    public static synchronized void fun()
    {
        代碼段
    }
    靜態變量或靜態方法加載到內存中時,內存中沒有本類對象,但一定有了該類對應的
    字節碼文件(類名.class),該對象的類型是class。靜態同步函數使用的同步鎖是所在類
    的字節碼文件。        

 1 class ThreadTest implements Runnable
 2 {
 3     private int ticket=50;
 4     public void run(){
 5         while(ticket>0){        
 6             String threadName = Thread.currentThread().getName();
 7             //同步代碼塊(越小越好)
 8             synchronized(this){
 9                 if(ticket>0){
10                     System.out.println(threadName + " sales ticket "+ticket);
11                     ticket--;
12                 }
13             }
14         }
15     }
16 }
17 
18 public class Thread03{
19     public static void main(String[] args){
20         ThreadTest t = new ThreadTest();
21         (new Thread(t, "窗口A")).start();
22         (new Thread(t, "窗口B")).start();
23         (new Thread(t, "窗口C")).start();
24         (new Thread(t, "窗口D")).start();
25     }
26 }

運行結果:

技術分享
窗口A sales ticket 50
窗口D sales ticket 49
窗口D sales ticket 48
窗口D sales ticket 47
窗口D sales ticket 46
窗口D sales ticket 45
窗口D sales ticket 44
窗口D sales ticket 43
窗口D sales ticket 42
窗口D sales ticket 41
窗口D sales ticket 40
窗口D sales ticket 39
窗口D sales ticket 38
窗口D sales ticket 37
窗口D sales ticket 36
窗口D sales ticket 35
窗口D sales ticket 34
窗口D sales ticket 33
窗口D sales ticket 32
窗口D sales ticket 31
窗口D sales ticket 30
窗口D sales ticket 29
窗口D sales ticket 28
窗口D sales ticket 27
窗口D sales ticket 26
窗口D sales ticket 25
窗口D sales ticket 24
窗口D sales ticket 23
窗口D sales ticket 22
窗口D sales ticket 21
窗口D sales ticket 20
窗口D sales ticket 19
窗口D sales ticket 18
窗口D sales ticket 17
窗口D sales ticket 16
窗口D sales ticket 15
窗口D sales ticket 14
窗口D sales ticket 13
窗口D sales ticket 12
窗口D sales ticket 11
窗口D sales ticket 10
窗口D sales ticket 9
窗口D sales ticket 8
窗口D sales ticket 7
窗口D sales ticket 6
窗口D sales ticket 5
窗口D sales ticket 4
窗口D sales ticket 3
窗口D sales ticket 2
窗口D sales ticket 1
View Code

4. 線程死鎖

死鎖現象即相互等待的局面
(操作系統中的死鎖) 指2個或多個進程在執行過程,因競爭資源而造成一種互相等待的局面,若無外力幹涉,進程無法推進下去。
發生死鎖的原因一般是兩個對象的鎖相互等待造成的。
那麽為什麽會產生死鎖呢?

  1. 因為系統資源不足。
  2. 進程運行推進的順序不合適。
  3. 資源分配不當。

產生死鎖的條件有四個:

  1. 互斥條件:所謂互斥就是進程在某一時間內獨占資源。
  2. 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
  3. 不剝奪條件:進程已獲得資源,在末使用完之前,不能強行剝奪。
  4. 循環等待條件:若幹進程之間形成一種頭尾相接的循環等待資源關系。

5. 單例模式

  特點:1.私有構造函數 2.在類中創建一個指向自己實例的私有靜態引用 3.以自己實例為返回類型值的靜態公有方法。

//餓漢式
class Singleton{
    private static final Singleton singleInstance = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return singleInstance;
    }
}
//懶漢式
class Singleton{
    private static Singleton singleInstance =null;
    private Singleton(){}
    public static synchronized Singleton getInstance(){
        if(singleInstance==null){
            singleInstance = new Singleton();
        }
        return singleInstance;
    }
}

6. 線程通信(wait()和notify()、notifyAll())

  線程的狀態

  技術分享

7. ReentrantLock和Condition

技術分享View Code

8. 停止線程的方法(interrupt()和isInterrupt())

9. 守護線程和join方法

  守護線程是為其他線程提供便利服務的,當全部的用戶線程結束後,守護線程才會隨JVM結束工作。 thread.setDaemon(true);

public static void main(String[] args){    
    Thread t3 = new Thread(test2, "線程t3");
    t3.start();
    t3.join();
    //主線程就此陷入等待,直到t3線程結束。
}

10. 線程優先級和yield方法

線程的優先級從低到高:1-10,優先級高的的優先執行,每個新線程都繼承了父線程的優先級,常量:Thread.MIN_PRIORITY 值為1,Thread.MAX_PRIORITY 值為10,Thread.NORM_PRIORITY 值為5

void setPriority(priority)
int getPriority()

  yield()方法將線程從執行狀態變成就緒狀態。

【Java基礎總結】多線程