激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

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

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

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

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

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

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

線程狀態(tài)圖

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

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

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

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

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

4. 阻塞狀態(tài)(Blocked)  : 阻塞狀態(tài)是線程因為某種原因放棄CPU使用權(quán),暫時停止運行。直到線程進入就緒狀態(tài),才有機會轉(zhuǎn)到運行狀態(tài)。阻塞的情況分三種:

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

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

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

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

 

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

Runnable:接口,實現(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) {
    // 啟動3個線程t1,t2,t3;每個線程各賣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本身是實現(xiàn)了Runnable接口的類。我們知道“一個類只能有一個父類,但是卻能實現(xiàn)多個接口”,因此Runnable具有更好的擴展性。此外,Runnable還可以用于“資源的共享”。即,多個線程都是基于某一個Runnable對象建立的,它們會共享Runnable對象上的資源。通常,建議通過“Runnable”實現(xiàn)多線程!

 

Thread的run與start

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

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

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

基本規(guī)則

第一條: 當一個線程訪問“某對象”的“synchronized方法”或者“synchronized代碼塊”時,其他線程對“該對象”的該“synchronized方法”或者“synchronized代碼塊”的訪問將被阻塞。

第二條: 當一個線程訪問“某對象”的“synchronized方法”或者“synchronized代碼塊”時,其他線程仍然可以訪問“該對象”的非同步代碼塊。

第三條: 當一個線程訪問“某對象”的“synchronized方法”或者“synchronized代碼塊”時,其他線程對“該對象”的其他的“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是指當前對象。也可以將this替換成其他對象,例如將this替換成obj,則foo2()在執(zhí)行synchronized(obj)時就獲取的是obj的同步鎖。

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

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

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

?
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() 不能被同時訪問。因為isSyncA()和isSyncB()都是訪問同一個對象(對象x)的同步鎖!

(02) x.isSyncA()與y.isSyncA()  可以同時被訪問。因為訪問的不是同一個對象的同步鎖,x.isSyncA()訪問的是x的同步鎖,而y.isSyncA()訪問的是y的同步鎖。

(03) x.cSyncA()與y.cSyncB()不能被同時訪問。因為cSyncA()和cSyncB()都是static類型,x.cSyncA()相當于Something.isSyncA(),y.cSyncB()相當于Something.isSyncB(),因此它們共用一個同步鎖,不能被同時反問。

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


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

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

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

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

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

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

wait(long timeout, int nanos)  -- 讓當前線程處于“等待(阻塞)狀態(tài)”,“直到其他線程調(diào)用此對象的 notify() 方法或 notifyAll() 方法,或者其他某個線程中斷當前線程,或者已超過某個實際時間量”,當前線程被喚醒(進入“就緒狀態(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()");
      // 喚醒當前的wait線程
      notify();
    }
  }
}
 
public class WaitTest {
 
  public static void main(String[] args) {
 
    ThreadA t1 = new ThreadA("t1");
 
    synchronized(t1) {
      try {
        // 啟動“線程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中啟動的“線程t1”。 而“鎖” 代表“t1這個對象的同步鎖”。

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

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

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

(05) “線程t1”運行完畢之后,釋放“當前對象的鎖”。緊接著,“主線程”獲取“t1對象的鎖”,然后接著運行。

t1.wait()是通過“線程t1”調(diào)用的wait()方法,但是調(diào)用t1.wait()的地方是在“主線程main”中。而主線程必須是“當前線程”,也就是運行狀態(tài),才可以執(zhí)行t1.wait()。所以,此時的“當前線程”是“主線程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對象鎖
          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) 主線程中新建并且啟動了3個線程"t1", "t2"和"t3"。

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

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

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

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

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

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

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

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


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

yield 與wait

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

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

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

 

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

 

sleep與wait的區(qū)別

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

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

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

 

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

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ù),當線程處于阻塞狀態(tài)時,調(diào)用線程的interrupt()產(chǎn)生InterruptedException中斷。中斷的捕獲在while(true)之外,這樣就退出了while(true)循環(huá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()保證,只要中斷標記為true就終止線程。
    while (!isInterrupted()) {
      // 執(zhí)行任務(wù)...
    }
  } catch (InterruptedException ie) {
    // 2. InterruptedException異常保證,當InterruptedException異常產(chǎn)生時,線程被終止。
  }
}


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

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

當Java虛擬機啟動時,通常有一個單一的非守護線程(該線程通過是通過main()方法啟動)。JVM會一直運行直到下面的任意一個條件發(fā)生,JVM就會終止運行:

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

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

守護進程

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

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

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

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

(03) 當消費者發(fā)現(xiàn)倉儲沒產(chǎn)品可消費時候會通知生產(chǎn)者生產(chǎn)。

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

線程之間的通信

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

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

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

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

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

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

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

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

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

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

 

ThreadLocal

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

ThreadLocal的一個實現(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 {
 
    //定義了一個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());
    }
}

事實上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();
}

 

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 婷婷亚洲一区二区三区 | 午夜神马电影网 | 日本一区二区三区精品 | 亚洲午夜精品视频 | 亚洲人成网在线观看 | 国产影视 | 成人在线网站 | 91美女视频在线 | 第一福利在线 | 亚洲午夜久久久精品一区二区三区 | 欧美一区二区三区免费不卡 | 看91视频 | 大奶一级片 | 久久网站热最新地址4 | 2021国产精品| 欧美成人一区在线观看 | 国产一区二区观看 | 国产精品99久久久久久久女警 | 操操日日| 黄色aaa视频| av电影在线观看网站 | 久久影院免费观看 | 国产精品免费av一区二区三区 | 日本一区二区三区四区高清视频 | 久久久久久久久久久国产精品 | 国产激爽大片在线播放 | 精品一区二区亚洲 | 成人小视频在线播放 | 99久久久精品国产一区二区 | 美国黄色小视频 | 免费黄色成人 | 久久精品视频网站 | 91短视频在线 | 精品国产精品久久 | 成人免费看毛片 | 大片毛片| 欧美日韩一区,二区,三区,久久精品 | 91九色丨porny丨国产jk | 精品亚洲一 | 久久逼逼 | www.成人免费视频 |