一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務(wù)器之家 - 編程語言 - JAVA教程 - 整理總結(jié)Java多線程程序編寫的要點(diǎn)

整理總結(jié)Java多線程程序編寫的要點(diǎn)

2020-03-25 13:32memristor JAVA教程

這篇文章主要介紹了Java多線程程序編寫的要點(diǎn),包括線程的狀態(tài)控制和優(yōu)先級(jí)以及線程的通信問題等方面,非常之全面!需要的朋友可以參考下

線程狀態(tài)圖

整理總結(jié)Java多線程程序編寫的要點(diǎn)

線程共包括以下5種狀態(tài)。

1. 新建狀態(tài)(New)         : 線程對(duì)象被創(chuàng)建后,就進(jìn)入了新建狀態(tài)。例如,Thread thread = new Thread()。

2. 就緒狀態(tài)(Runnable): 也被稱為“可執(zhí)行狀態(tài)”。線程對(duì)象被創(chuàng)建后,其它線程調(diào)用了該對(duì)象的start()方法,從而來啟動(dòng)該線程。例如,thread.start()。處于就緒狀態(tài)的線程,隨時(shí)可能被CPU調(diào)度執(zhí)行。

3. 運(yùn)行狀態(tài)(Running) : 線程獲取CPU權(quán)限進(jìn)行執(zhí)行。需要注意的是,線程只能從就緒狀態(tài)進(jìn)入到運(yùn)行狀態(tài)。

4. 阻塞狀態(tài)(Blocked)  : 阻塞狀態(tài)是線程因?yàn)槟撤N原因放棄CPU使用權(quán),暫時(shí)停止運(yùn)行。直到線程進(jìn)入就緒狀態(tài),才有機(jī)會(huì)轉(zhuǎn)到運(yùn)行狀態(tài)。阻塞的情況分三種:

    (01) 等待阻塞 -- 通過調(diào)用線程的wait()方法,讓線程等待某工作的完成。

    (02) 同步阻塞 -- 線程在獲取synchronized同步鎖失敗(因?yàn)殒i被其它線程所占用),它會(huì)進(jìn)入同步阻塞狀態(tài)。

    (03) 其他阻塞 -- 通過調(diào)用線程的sleep()或join()或發(fā)出了I/O請(qǐng)求時(shí),線程會(huì)進(jìn)入到阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時(shí)、join()等待線程終止或者超時(shí)、或者I/O處理完畢時(shí),線程重新轉(zhuǎn)入就緒狀態(tài)。

5. 死亡狀態(tài)(Dead)    : 線程執(zhí)行完了或者因異常退出了run()方法,該線程結(jié)束生命周期。

 

實(shí)現(xiàn)多線程的方式Thread和Runnable
Thread:繼承thread類,實(shí)現(xiàn)run方法,在main函數(shù)中調(diào)用start方法啟動(dòng)線程

Runnable:接口,實(shí)現(xiàn)Runnable接口,作為參數(shù)傳遞給Thread的構(gòu)造函數(shù),在main中調(diào)用start方法

例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class task implements Runnable {
 
  private int ticket = 10;
 
  @Override
  public void run() {
    for (int i = 0; i < 20; i++) {
      if (this.ticket > 0) {
        System.out.println(Thread.currentThread().getName()
            + " sold ticket " + this.ticket--);
      }
    }
  }
 
};
 
 
public class RunnableTest {
  public static void main(String[]args){
    task mytask = new task();
    Thread t1 = new Thread(mytask);
    Thread t2 = new Thread(mytask);
    Thread t3 = new Thread(mytask);
    t1.start();
    t2.start();
    t3.start();
  }
 
}
 
//ThreadTest.java 源碼
class MyThread extends Thread {
  private int ticket = 10;
 
  public void run() {
    for (int i = 0; i < 20; i++) {
      if (this.ticket > 0) {
        System.out.println(this.getName() + " 賣票:ticket"
            + this.ticket--);
      }
    }
  }
}
 
public class ThreadTest {
  public static void main(String[] args) {
    // 啟動(dòng)3個(gè)線程t1,t2,t3;每個(gè)線程各賣10張票!
    MyThread t1 = new MyThread();
    MyThread t2 = new MyThread();
    MyThread t3 = new MyThread();
    t1.start();
    t2.start();
    t3.start();
  }
};


Thread與Runnable的區(qū)別

Thread 是類,而Runnable是接口;Thread本身是實(shí)現(xiàn)了Runnable接口的類。我們知道“一個(gè)類只能有一個(gè)父類,但是卻能實(shí)現(xiàn)多個(gè)接口”,因此Runnable具有更好的擴(kuò)展性。此外,Runnable還可以用于“資源的共享”。即,多個(gè)線程都是基于某一個(gè)Runnable對(duì)象建立的,它們會(huì)共享Runnable對(duì)象上的資源。通常,建議通過“Runnable”實(shí)現(xiàn)多線程!

 

Thread的run與start

start() : 它的作用是啟動(dòng)一個(gè)新線程,新線程會(huì)執(zhí)行相應(yīng)的run()方法。start()不能被重復(fù)調(diào)用。start()實(shí)際上是通過本地方法start0()啟動(dòng)線程的。而start0()會(huì)新運(yùn)行一個(gè)線程,新線程會(huì)調(diào)用run()方法。

run()   : run()就和普通的成員方法一樣,可以被重復(fù)調(diào)用。單獨(dú)調(diào)用run()的話,會(huì)在當(dāng)前線程中執(zhí)行run(),而并不會(huì)啟動(dòng)新線程!run()就是直接調(diào)用Thread線程的Runnable成員的run()方法,并不會(huì)新建一個(gè)線程。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Demo.java 的源碼
class MyThread extends Thread{
  public MyThread(String name) {
    super(name);
  }
 
  public void run(){
    System.out.println(Thread.currentThread().getName()+" is running");
  }
};
 
public class Demo {
  public static void main(String[] args) {
    Thread mythread=new MyThread("mythread");
 
    System.out.println(Thread.currentThread().getName()+" call mythread.run()");
    mythread.run();
 
    System.out.println(Thread.currentThread().getName()+" call mythread.start()");
    mythread.start();
  }
}

輸出:

?
1
2
3
4
main call mythread.run()
main is running
main call mythread.start()
mythread is running


synchronized
在java中每個(gè)對(duì)象都有一個(gè)同步鎖,當(dāng)我們調(diào)用對(duì)象的synchronized方法就獲取了對(duì)象鎖,synchronized(obj)就獲取了“obj這個(gè)對(duì)象”的同步鎖.不同線程對(duì)同步鎖的訪問是互斥的.某時(shí)間點(diǎn)對(duì)象的同步鎖只能被一個(gè)線程獲取到.通過同步鎖,我們就能在多線程中,實(shí)現(xiàn)對(duì)“對(duì)象/方法”的互斥訪問.  例如,現(xiàn)在有兩個(gè)線程A和線程B,它們都會(huì)訪問“對(duì)象obj的同步鎖”。假設(shè),在某一時(shí)刻,線程A獲取到“obj的同步鎖”并在執(zhí)行一些操作;而此時(shí),線程B也企圖獲取“obj的同步鎖” —— 線程B會(huì)獲取失敗,它必須等待,直到線程A釋放了“該對(duì)象的同步鎖”之后線程B才能獲取到“obj的同步鎖”從而才可以運(yùn)行。

基本規(guī)則

第一條: 當(dāng)一個(gè)線程訪問“某對(duì)象”的“synchronized方法”或者“synchronized代碼塊”時(shí),其他線程對(duì)“該對(duì)象”的該“synchronized方法”或者“synchronized代碼塊”的訪問將被阻塞。

第二條: 當(dāng)一個(gè)線程訪問“某對(duì)象”的“synchronized方法”或者“synchronized代碼塊”時(shí),其他線程仍然可以訪問“該對(duì)象”的非同步代碼塊。

第三條: 當(dāng)一個(gè)線程訪問“某對(duì)象”的“synchronized方法”或者“synchronized代碼塊”時(shí),其他線程對(duì)“該對(duì)象”的其他的“synchronized方法”或者“synchronized代碼塊”的訪問將被阻塞。

synchronized 方法

?
1
2
3
4
5
6
7
8
9
10
public synchronized void foo1() {
  System.out.println("synchronized methoed");
}
synchronized代碼塊
 
public void foo2() { 
    synchronized (this) {
    System.out.println("synchronized methoed");
    }
}

synchronized代碼塊中的this是指當(dāng)前對(duì)象。也可以將this替換成其他對(duì)象,例如將this替換成obj,則foo2()在執(zhí)行synchronized(obj)時(shí)就獲取的是obj的同步鎖。

synchronized代碼塊可以更精確的控制沖突限制訪問區(qū)域,有時(shí)候表現(xiàn)更高效率

實(shí)例鎖 和 全局鎖
實(shí)例鎖 -- 鎖在某一個(gè)實(shí)例對(duì)象上。如果該類是單例,那么該鎖也具有全局鎖的概念。實(shí)例鎖對(duì)應(yīng)的就是synchronized關(guān)鍵字。

全局鎖 -- 該鎖針對(duì)的是類,無論實(shí)例多少個(gè)對(duì)象,那么線程都共享該鎖。全局鎖對(duì)應(yīng)的就是static synchronized(或者是鎖在該類的class或者classloader對(duì)象上)。

?
1
2
3
4
5
6
pulbic class Something { 
  public synchronized void isSyncA(){} 
  public synchronized void isSyncB(){} 
  public static synchronized void cSyncA(){} 
  public static synchronized void cSyncB(){}
}


(01) x.isSyncA()與x.isSyncB() 不能被同時(shí)訪問。因?yàn)閕sSyncA()和isSyncB()都是訪問同一個(gè)對(duì)象(對(duì)象x)的同步鎖!

(02) x.isSyncA()與y.isSyncA()  可以同時(shí)被訪問。因?yàn)樵L問的不是同一個(gè)對(duì)象的同步鎖,x.isSyncA()訪問的是x的同步鎖,而y.isSyncA()訪問的是y的同步鎖。

(03) x.cSyncA()與y.cSyncB()不能被同時(shí)訪問。因?yàn)閏SyncA()和cSyncB()都是static類型,x.cSyncA()相當(dāng)于Something.isSyncA(),y.cSyncB()相當(dāng)于Something.isSyncB(),因此它們共用一個(gè)同步鎖,不能被同時(shí)反問。

(04) x.isSyncA()與Something.cSyncA() 可以被同時(shí)訪問。因?yàn)閕sSyncA()是實(shí)例方法,x.isSyncA()使用的是對(duì)象x的鎖;而cSyncA()是靜態(tài)方法,Something.cSyncA()可以理解對(duì)使用的是“類的鎖”。因此,它們是可以被同時(shí)訪問的。


線程阻塞與喚醒wait,notify,notifyAll
在Object.java中,定義了wait(), notify()和notifyAll()等接口。wait()的作用是讓當(dāng)前線程進(jìn)入等待狀態(tài),同時(shí),wait()也會(huì)讓當(dāng)前線程釋放它所持有的鎖。而notify()和notifyAll()的作用,則是喚醒當(dāng)前對(duì)象上的等待線程;notify()是喚醒單個(gè)線程,而notifyAll()是喚醒所有的線程。

Object類中關(guān)于等待/喚醒的API詳細(xì)信息如下:

notify()       -- 喚醒在此對(duì)象監(jiān)視器上等待的單個(gè)線程。

notifyAll()   -- 喚醒在此對(duì)象監(jiān)視器上等待的所有線程。

wait()         -- 讓當(dāng)前線程處于“等待(阻塞)狀態(tài)”,“直到其他線程調(diào)用此對(duì)象的 notify() 方法或 notifyAll() 方法”,當(dāng)前線程被喚醒(進(jìn)入“就緒狀態(tài)”)。

wait(long timeout)      -- 讓當(dāng)前線程處于“等待(阻塞)狀態(tài)”,“直到其他線程調(diào)用此對(duì)象的 notify() 方法或 notifyAll() 方法,或者超過指定的時(shí)間量”,當(dāng)前線程被喚醒(進(jìn)入“就緒狀態(tài)”)。

wait(long timeout, int nanos)  -- 讓當(dāng)前線程處于“等待(阻塞)狀態(tài)”,“直到其他線程調(diào)用此對(duì)象的 notify() 方法或 notifyAll() 方法,或者其他某個(gè)線程中斷當(dāng)前線程,或者已超過某個(gè)實(shí)際時(shí)間量”,當(dāng)前線程被喚醒(進(jìn)入“就緒狀態(tài)”)。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// WaitTest.java的源碼
class ThreadA extends Thread{
 
  public ThreadA(String name) {
    super(name);
  }
 
  public void run() {
    synchronized (this) {
      System.out.println(Thread.currentThread().getName()+" call notify()");
      // 喚醒當(dāng)前的wait線程
      notify();
    }
  }
}
 
public class WaitTest {
 
  public static void main(String[] args) {
 
    ThreadA t1 = new ThreadA("t1");
 
    synchronized(t1) {
      try {
        // 啟動(dòng)“線程t1”
        System.out.println(Thread.currentThread().getName()+" start t1");
        t1.start();
 
        // 主線程等待t1通過notify()喚醒。
        System.out.println(Thread.currentThread().getName()+" wait()");
        t1.wait();
 
        System.out.println(Thread.currentThread().getName()+" continue");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

輸出

?
1
2
3
4
main start t1
main wait()
t1 call notify()
main continue

(01) 注意,圖中"主線程" 代表“主線程main”。"線程t1" 代表WaitTest中啟動(dòng)的“線程t1”。 而“鎖” 代表“t1這個(gè)對(duì)象的同步鎖”。

(02) “主線程”通過 new ThreadA("t1") 新建“線程t1”。隨后通過synchronized(t1)獲取“t1對(duì)象的同步鎖”。然后調(diào)用t1.start()啟動(dòng)“線程t1”。

(03) “主線程”執(zhí)行t1.wait() 釋放“t1對(duì)象的鎖”并且進(jìn)入“等待(阻塞)狀態(tài)”。等待t1對(duì)象上的線程通過notify() 或 notifyAll()將其喚醒。

(04) “線程t1”運(yùn)行之后,通過synchronized(this)獲取“當(dāng)前對(duì)象的鎖”;接著調(diào)用notify()喚醒“當(dāng)前對(duì)象上的等待線程”,也就是喚醒“主線程”。

(05) “線程t1”運(yùn)行完畢之后,釋放“當(dāng)前對(duì)象的鎖”。緊接著,“主線程”獲取“t1對(duì)象的鎖”,然后接著運(yùn)行。

t1.wait()是通過“線程t1”調(diào)用的wait()方法,但是調(diào)用t1.wait()的地方是在“主線程main”中。而主線程必須是“當(dāng)前線程”,也就是運(yùn)行狀態(tài),才可以執(zhí)行t1.wait()。所以,此時(shí)的“當(dāng)前線程”是“主線程main”!因此,t1.wait()是讓“主線程”等待,而不是“線程t1”!

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package thread.Test;
 
public class NotifyAllTest {
 
  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();//在此喚醒t1.t2.t3
    }
  }
 
  static class ThreadA extends Thread{
 
    public ThreadA(String name){
      super(name);
    }
 
    public void run() {
      synchronized (obj) {
        try {
          // 打印輸出結(jié)果
          System.out.println(Thread.currentThread().getName() + " wait");
 
          //釋放obj對(duì)象鎖
          obj.wait();
 
          // 打印輸出結(jié)果
          System.out.println(Thread.currentThread().getName() + " continue");
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }
}

輸出:

?
1
2
3
4
5
6
7
8
t1 wait
main sleep(3000)
t3 wait
t2 wait
main notifyAll()
t2 continue
t3 continue
t1 continue


(01) 主線程中新建并且啟動(dòng)了3個(gè)線程"t1", "t2"和"t3"。

(02) 主線程通過sleep(3000)休眠3秒。在主線程休眠3秒的過程中,我們假設(shè)"t1", "t2"和"t3"這3個(gè)線程都運(yùn)行了。以"t1"為例,當(dāng)它運(yùn)行的時(shí)候,它會(huì)執(zhí)行obj.wait()等待其它線程通過notify()或額nofityAll()來喚醒它;相同的道理,"t2"和"t3"也會(huì)等待其它線程通過nofity()或nofityAll()來喚醒它們。

(03) 主線程休眠3秒之后,接著運(yùn)行。執(zhí)行 obj.notifyAll() 喚醒obj上的等待線程,即喚醒"t1", "t2"和"t3"這3個(gè)線程。 緊接著,主線程的synchronized(obj)運(yùn)行完畢之后,主線程釋放“obj鎖”。這樣,"t1", "t2"和"t3"就可以獲取“obj鎖”而繼續(xù)運(yùn)行了!

notify,notifyall與鎖的關(guān)系

Object中的wait(), notify()等函數(shù),和synchronized一樣,會(huì)對(duì)“對(duì)象的同步鎖”進(jìn)行操作。

wait()會(huì)使“當(dāng)前線程”等待,因?yàn)榫€程進(jìn)入等待狀態(tài),所以線程應(yīng)該釋放它鎖持有的“同步鎖”,否則其它線程獲取不到該“同步鎖”而無法運(yùn)行!

OK,線程調(diào)用wait()之后,會(huì)釋放它鎖持有的“同步鎖”;而且,根據(jù)前面的介紹,我們知道:等待線程可以被notify()或notifyAll()喚醒。現(xiàn)在,請(qǐng)思考一個(gè)問題:notify()是依據(jù)什么喚醒等待線程的?或者說,wait()等待線程和notify()之間是通過什么關(guān)聯(lián)起來的?答案是:依據(jù)“對(duì)象的同步鎖”。

負(fù)責(zé)喚醒等待線程的那個(gè)線程(我們稱為“喚醒線程”),它只有在獲取“該對(duì)象的同步鎖”(這里的同步鎖必須和等待線程的同步鎖是同一個(gè)),并且調(diào)用notify()或notifyAll()方法之后,才能喚醒等待線程。雖然,等待線程被喚醒;但是,它不能立刻執(zhí)行,因?yàn)閱拘丫€程還持有“該對(duì)象的同步鎖”。必須等到喚醒線程釋放了“對(duì)象的同步鎖”之后,等待線程才能獲取到“對(duì)象的同步鎖”進(jìn)而繼續(xù)運(yùn)行。

總之,notify(), wait()依賴于“同步鎖”,而“同步鎖”是對(duì)象鎖持有,并且每個(gè)對(duì)象有且僅有一個(gè)!這就是為什么notify(), wait()等函數(shù)定義在Object類,而不是Thread類中的原因。


線程讓步y(tǒng)ield
線程讓步,使線程從執(zhí)行狀態(tài)變?yōu)榫途w狀態(tài),從而讓其它具有相同優(yōu)先級(jí)的等待線程獲取執(zhí)行權(quán);但是,并不能保證在當(dāng)前線程調(diào)用yield()之后,其它具有相同優(yōu)先級(jí)的線程就一定能獲得執(zhí)行權(quán);也有可能是當(dāng)前線程又進(jìn)入到“運(yùn)行狀態(tài)”繼續(xù)運(yùn)行。

yield 與wait

(01) wait()是讓線程由“運(yùn)行狀態(tài)”進(jìn)入到“等待(阻塞)狀態(tài)”,而不yield()是讓線程由“運(yùn)行狀態(tài)”進(jìn)入到“就緒狀態(tài)”。

(02) wait()是會(huì)線程釋放它所持有對(duì)象的同步鎖,而yield()方法不會(huì)釋放鎖。

(03) wait是object的方法,yield是Thread的方法

 

線程休眠 sleep
sleep() 的作用是讓當(dāng)前線程休眠,即當(dāng)前線程會(huì)從“運(yùn)行狀態(tài)”進(jìn)入到“休眠(阻塞)狀態(tài)”。sleep()會(huì)指定休眠時(shí)間,線程休眠的時(shí)間會(huì)大于/等于該休眠時(shí)間;在線程重新被喚醒時(shí),它會(huì)由“阻塞狀態(tài)”變成“就緒狀態(tài)”,從而等待cpu的調(diào)度執(zhí)行。

 

sleep與wait的區(qū)別

wait()的作用是讓當(dāng)前線程由“運(yùn)行狀態(tài)”進(jìn)入“等待(阻塞)狀態(tài)”的同時(shí),也會(huì)釋放同步鎖。而sleep()的作用是也是讓當(dāng)前線程由“運(yùn)行狀態(tài)”進(jìn)入到“休眠(阻塞)狀態(tài)。(這個(gè)其實(shí)區(qū)別不大)

wait()會(huì)釋放對(duì)象的同步鎖,而sleep()則不會(huì)釋放鎖

 wait是object的方法,sleep是Thread的方法

 

join
讓主線程等待,子線程運(yùn)行完畢,主線程才能繼續(xù)運(yùn)行

interrupt
用來終止處于阻塞狀態(tài)的線程

?
1
2
3
4
5
6
7
8
9
10
@Override
public void run() {
  try {
    while (true) {
      // 執(zhí)行任務(wù)...
    }
  } catch (InterruptedException ie) {
    // 由于產(chǎn)生InterruptedException異常,退出while(true)循環(huán),線程終止!
  }
}

在while(true)中不斷的執(zhí)行任務(wù),當(dāng)線程處于阻塞狀態(tài)時(shí),調(diào)用線程的interrupt()產(chǎn)生InterruptedException中斷。中斷的捕獲在while(true)之外,這樣就退出了while(true)循環(huán)

終止處于運(yùn)行狀態(tài)的線程

?
1
2
3
4
5
6
@Override
public void run() { 
  while (!isInterrupted()) {   
  // 執(zhí)行任務(wù)... 
  }
}

通用的終止線程的方式

?
1
2
3
4
5
6
7
8
9
10
11
@Override
public void run() {
  try {
    // 1. isInterrupted()保證,只要中斷標(biāo)記為true就終止線程。
    while (!isInterrupted()) {
      // 執(zhí)行任務(wù)...
    }
  } catch (InterruptedException ie) {
    // 2. InterruptedException異常保證,當(dāng)InterruptedException異常產(chǎn)生時(shí),線程被終止。
  }
}


線程優(yōu)先級(jí)
java 中的線程優(yōu)先級(jí)的范圍是1~10,默認(rèn)的優(yōu)先級(jí)是5。“高優(yōu)先級(jí)線程”會(huì)優(yōu)先于“低優(yōu)先級(jí)線程”執(zhí)行。java 中有兩種線程:用戶線程和守護(hù)線程。可以通過isDaemon()方法來區(qū)別它們:如果返回false,則說明該線程是“用戶線程”;否則就是“守護(hù)線程”。用戶線程一般用戶執(zhí)行用戶級(jí)任務(wù),而守護(hù)線程也就是“后臺(tái)線程”,一般用來執(zhí)行后臺(tái)任務(wù)。需要注意的是:Java虛擬機(jī)在“用戶線程”都結(jié)束后會(huì)后退出。

每個(gè)線程都有一個(gè)優(yōu)先級(jí)。“高優(yōu)先級(jí)線程”會(huì)優(yōu)先于“低優(yōu)先級(jí)線程”執(zhí)行。每個(gè)線程都可以被標(biāo)記為一個(gè)守護(hù)進(jìn)程或非守護(hù)進(jìn)程。在一些運(yùn)行的主線程中創(chuàng)建新的子線程時(shí),子線程的優(yōu)先級(jí)被設(shè)置為等于“創(chuàng)建它的主線程的優(yōu)先級(jí)”,當(dāng)且僅當(dāng)“創(chuàng)建它的主線程是守護(hù)線程”時(shí)“子線程才會(huì)是守護(hù)線程”。

當(dāng)Java虛擬機(jī)啟動(dòng)時(shí),通常有一個(gè)單一的非守護(hù)線程(該線程通過是通過main()方法啟動(dòng))。JVM會(huì)一直運(yùn)行直到下面的任意一個(gè)條件發(fā)生,JVM就會(huì)終止運(yùn)行:

(01) 調(diào)用了exit()方法,并且exit()有權(quán)限被正常執(zhí)行。

(02) 所有的“非守護(hù)線程”都死了(即JVM中僅僅只有“守護(hù)線程”)。

守護(hù)進(jìn)程

(01) 主線程main是用戶線程,它創(chuàng)建的子線程t1也是用戶線程。

(02) t2是守護(hù)線程。在“主線程main”和“子線程t1”(它們都是用戶線程)執(zhí)行完畢,只剩t2這個(gè)守護(hù)線程的時(shí)候,JVM自動(dòng)退出。

生產(chǎn)者消費(fèi)者問題
(01) 生產(chǎn)者僅僅在倉儲(chǔ)未滿時(shí)候生產(chǎn),倉滿則停止生產(chǎn)。

(02) 消費(fèi)者僅僅在倉儲(chǔ)有產(chǎn)品時(shí)候才能消費(fèi),倉空則等待。

(03) 當(dāng)消費(fèi)者發(fā)現(xiàn)倉儲(chǔ)沒產(chǎn)品可消費(fèi)時(shí)候會(huì)通知生產(chǎn)者生產(chǎn)。

(04) 生產(chǎn)者在生產(chǎn)出可消費(fèi)產(chǎn)品時(shí)候,應(yīng)該通知等待的消費(fèi)者去消費(fèi)。

線程之間的通信

方式:共享內(nèi)存和消息傳遞

共享內(nèi)存:線程A和線程B共享內(nèi)存,線程A更新共享變量的值,刷新到主內(nèi)存中,線程B去主內(nèi)存中讀取線程A更新后的變量。整個(gè)通信過程必須通過主內(nèi)存。同步是顯式進(jìn)行的。

整理總結(jié)Java多線程程序編寫的要點(diǎn)

如果一個(gè)變量是volatile類型,則對(duì)該變量的讀寫就將具有原子性。如果是多個(gè)volatile操作或類似于volatile++這種復(fù)合操作,這些操作整體上不具有原子性。

volatile變量自身具有下列特性:

[可見性]:對(duì)一個(gè)volatile變量的讀,總是能看到(任意線程)對(duì)這個(gè)volatile變量最后的寫入。

[原子性]:對(duì)任意單個(gè)volatile變量的讀/寫具有原子性,但類似于volatile++這種復(fù)合操作不具有原子性。

volatile寫:當(dāng)寫一個(gè)volatile變量時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存中的共享變量刷新到主內(nèi)存。

volatile讀:當(dāng)讀一個(gè)volatile變量時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存置為無效。線程接下來將從主內(nèi)存中讀取共享變量。

消息傳遞:消息的發(fā)送在消息的接受之前,同步隱式進(jìn)行。

 

ThreadLocal

ThreadLocal 不是用來解決共享對(duì)象的多線程訪問問題的,一般情況下,通過ThreadLocal.set() 到線程中的對(duì)象是該線程自己使用的對(duì)象,其他線程是不需要訪問的,也訪問不到的.ThreadLocal使得各線程能夠保持各自獨(dú)立的一個(gè)對(duì)象,并不是通過ThreadLocal.set()來實(shí)現(xiàn)的,而是通過每個(gè)線程中的new 對(duì)象 的操作來創(chuàng)建的對(duì)象,每個(gè)線程創(chuàng)建一個(gè),不是什么對(duì)象的拷貝或副本。通過ThreadLocal.set()將這個(gè)新創(chuàng)建的對(duì)象的引用保存到各線程的自己的一個(gè)map中,每個(gè)線程都有這樣一個(gè)map,執(zhí)行ThreadLocal.get()時(shí),各線程從自己的map中取出放進(jìn)去的對(duì)象,因此取出來的是各自自己線程中的對(duì)象,ThreadLocal實(shí)例是作為map的key來使用的。 如果ThreadLocal.set()進(jìn)去的東西本來就是多個(gè)線程共享的同一個(gè)對(duì)象,那么多個(gè)線程的ThreadLocal.get()取得的還是這個(gè)共享對(duì)象本身,還是有并發(fā)訪問問題。

ThreadLocal的一個(gè)實(shí)現(xiàn)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
 
/**
* 使用了ThreadLocal的類
*
* @author leizhimin 2010-1-5 10:35:27
*/
public class MyThreadLocal {
 
    //定義了一個(gè)ThreadLocal變量,用來保存int或Integer數(shù)據(jù)
    private com.lavasoft.test2.ThreadLocal<Integer> tl = new com.lavasoft.test2.ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };
 
    public Integer getNextNum() {
        //將tl的值獲取后加1,并更新設(shè)置t1的值
        tl.set(tl.get() + 1);
        return tl.get();
    }
}
 
class ThreadLocal<T> {
    private Map<Thread, T> map = Collections.synchronizedMap(new HashMap<Thread, T>());
 
    public ThreadLocal() {
    }
 
    protected T initialValue() {
        return null;
    }
 
    public T get() {
        Thread t = Thread.currentThread();
        T obj = map.get(t);
        if (obj == null && !map.containsKey(t)) {
            obj = initialValue();
            map.put(t, obj);
        }
        return obj;
    }
 
    public void set(T value) {
        map.put(Thread.currentThread(), value);
    }
 
    public void remove() {
        map.remove(Thread.currentThread());
    }
}

事實(shí)上ThreadLocal是這樣做的:

?
1
2
3
4
5
6
7
8
9
10
public T get() {
  Thread t = Thread.currentThread();
  ThreadLocalMap map = getMap(t);
  if (map != null) {
    ThreadLocalMap.Entry e = map.getEntry(this);
    if (e != null)
      return (T)e.value;
  }
  return setInitialValue();
}

 

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美视频在线播放观看免费福利资源 | 日韩精品中文字幕视频一区 | 国产精品视频久久久久 | 亚洲国产欧美久久香综合 | 天海翼最新 | 999精品视频这里只有精品 | 午夜伦理电影在线观免费 | porono日本动漫 | 天天色综 | haodiaose在线精品免费视频 | 偷偷操不一样 | 久久成人a毛片免费观看网站 | 欧美第一视频 | 精品久久久久久影院免费 | 精品一区二区国语对白 | 男gay网站视频免费观看 | 亚洲区在线播放 | 果冻传媒九一制片厂网站 | 国产精品俺来也在线观看了 | 欧美成人一区二区 | 国产在线观看a | tube性睡觉hd | 精品亚洲综合在线第一区 | 欧美日韩导航 | 高h生子双性美人受 | 青草悠悠视频在线观看 | 国产精品久久久久久久久免费观看 | 日本高清动作片www欧美 | 日本伦理动漫在线观看 | 爆操美女| 免费在线视频成人 | 日本中文字幕永久在线 | 久久91精品国产91 | 无人区大片免费播放器 | 亚洲午夜久久久久国产 | 喜爱夜蒲2三级做爰 | 亚洲日本中文字幕在线2022 | 视频一本大道香蕉久在线播放 | 亚洲第一在线播放 | 日本 视频 在线 | 男人天堂网www |