Java線程學習詳解

線程基礎

1. 線程的生命週期

1.1 新建狀態:

  • 使用 new 關鍵字和 Thread 類或其子類創建一個線程對象後,該線程對象就處於新建狀態。它保持這個狀態直到程序 start() 這個線程。

1.2 就緒狀態:

  • 當線程對象調用了start()方法以後,該線程就進入就緒狀態。就緒狀態的線程處於就緒隊列中,要等待JVM裏線程調度器的調度。

1.3 運行狀態:

  • 若是就緒狀態的線程獲取 CPU 資源,就能夠執行 run(),此時線程便處於運行狀態。處於運行狀態的線程最爲複雜,它能夠變爲阻塞狀態、就緒狀態和死亡狀態。

1.4 阻塞狀態:

  • 若是一個線程執行了sleep(睡眠)、suspend(掛起)等方法,失去所佔用資源以後,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或得到設備資源後能夠從新進入就緒狀態。能夠分爲三種:
    • 等待阻塞:運行狀態中的線程執行 wait() 方法,使線程進入到等待阻塞狀態。
    • 同步阻塞:線程在獲取 synchronized 同步鎖失敗(由於同步鎖被其餘線程佔用)。
    • 其餘阻塞:經過調用線程的 sleep() 或 join() 發出了 I/O 請求時,線程就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待線程終止或超時,或者 I/O 處理完畢,線程從新轉入就緒狀態。

1.5 死亡狀態:

  • 一個運行狀態的線程完成任務或者其餘終止條件發生時,該線程就切換到終止狀態。

2. 線程的優先級和守護線程

2.1 線程的優先級

  • 每個 Java 線程都有一個優先級,這樣有助於操做系統肯定線程的調度順序。
  • Java 線程的優先級是一個整數,其取值範圍是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )
  • 默認狀況下,每個線程都會分配一個優先級 NORM_PRIORITY(5)

2.2 守護線程

  • Java中有兩種線程:用戶線程和守護線程。能夠經過isDeamon()方法來區別它們:若是返回false,則說明該線程是「用戶線程」;不然就是「守護線程」。
  • 用戶線程通常用戶執行用戶級任務,而守護線程也就是「後臺線程」,通常用來執行後臺任務。
  • 須要注意的是:JVM在「用戶線程」都結束後會退出。

3. 建立線程

3.1 經過實現 Runnable 接口

  • 步驟:
    • 建立類實現 Runnable 接口
    • 實現 run() 方法,線程實際運行的方法
    • 實現 start() 方法,裏面實例化線程對象(new Thread(this, threadName)),調用線程對象的 start() 方法
  • 代碼實現
    ```java
    package com.ljw.thread;java

    public class RunnableDemo {安全

    public static void main(String[] args) {
          // 測試
          RunnableDemo R = new RunnableDemo();
          RunnableThread R1 = R.new RunnableThread("thread1");
          R1.start();
    
          RunnableThread R2 = R.new RunnableThread("thread2");
          R2.start();
    
      }
    
    
      class RunnableThread implements Runnable{
          private String threadName;
          private Thread t;
    
          public RunnableThread(String name) {
              // TODO Auto-generated constructor stub
              threadName = name;
              System.out.println("建立線程 "+threadName);
          }
    
    
          @Override
          public void run() {
              System.out.println("正在運行線程:"+threadName);
    
              try {
                  for(int i=10;i>0;i--) {
                      System.out.println("線程:"+threadName+" 正在打印:"+i);    
                      Thread.sleep(50);
                  }
              }catch(Exception e) {
                  e.printStackTrace();
              }
    
              System.out.println("線程:"+threadName+" 正在退出......");
          }
    
          public void start() {
              System.out.println("開始線程 "+threadName);
              if(t == null) {
                  t = new Thread(this, threadName);
                  t.start();
              }
          }
    
      }

    }併發

    ```ide

3.2 經過繼承 Thread 類自己

  • 步驟:
    • 建立類繼承 Thread 類
    • 下面與用Runnable接口同樣

3.3 經過 Callable 和 Future 建立線程

  • 步驟:
    • 建立 Callable 接口的實現類,並實現 call() 方法,該 call() 方法將做爲線程執行體,而且有返回值。
    • 建立 Callable 實現類的實例,使用 FutureTask 類來包裝 Callable 對象,該 FutureTask 對象封裝了該 Callable 對象的 call() 方法的返回值。
    • 使用 FutureTask 對象做爲 Thread 對象的 target 建立並啓動新線程。
    • 調用 FutureTask 對象的 get() 方法來得到子線程執行結束後的返回值。
  • Callable接口與Runnable接口的區別:
    • Callable中call方法能夠有返回值,而Runnable中的run方法沒有返回值
  • 代碼實現
    ```java
    package com.ljw.thread;工具

    import java.util.concurrent.Callable;
    import java.util.concurrent.FutureTask;oop

    public class CallableThreadTest implements Callable {
    public static void main(String[] args)
    {
    CallableThreadTest ctt = new CallableThreadTest();
    FutureTask ft = new FutureTask<>(ctt);
    for(int i = 0;i < 10;i++)
    {
    System.out.println(Thread.currentThread().getName()+" 的循環變量i的值"+i);
    if(i%2==0)
    {
    new Thread(ft,"有返回值的線程").start();
    }
    }
    try
    {
    System.out.println("子線程的返回值:"+ft.get());
    } catch (InterruptedException e)
    {
    e.printStackTrace();
    } catch (Exception e)
    {
    e.printStackTrace();
    }
    性能

    }
      @Override  
      public Integer call() throws Exception  
      {  
          int i = 0;  
          for(;i<10;i++)  
          {  
              System.out.println(Thread.currentThread().getName()+" "+i);  
          }  
          return i;  
      }

    }
    ```測試

4. synchronized關鍵字

4.1 概述

  • synchronized關鍵字是爲了解決共享資源競爭的問題,共享資源通常是以對象形式存在的內存片斷,但也能夠是文件、輸入/輸出端口,或者是打印機。
  • 要控制對共享資源的訪問,得先把它包裝進一個對象。而後把全部要訪問的這個資源的方法標記爲synchronized。
  • 若是某個任務處於一個對標記爲synchronized的方法的調用中,那麼在這個線程從該方法返回以前,其餘全部要調用類中任何標記爲synchronized方法的線程都會被阻塞。

4.2 基本原則

  • 第一條:當一個線程訪問某對象的synchronized方法或者synchronized代碼塊時,其餘線程對該對象的該synchronized方法或者synchronized代碼塊的訪問將被阻塞。
  • 第二條:當一個線程訪問某對象的synchronized方法或者synchronized代碼塊時,其餘線程仍然能夠訪問該對象的非同步代碼塊。
  • 第三條:當一個線程訪問某對象的synchronized方法或者synchronized代碼塊時,其餘線程對該對象的其餘的synchronized方法或者synchronized代碼塊的訪問將被阻塞。

4.3 實例

  • 兩個類似的例子
    • 實例1:實現接口Runnable
    package com.ljw.thread;
    
    public class RunnableTest {
    
        public static void main(String[] args) {
    
            class MyRunnable implements Runnable{
    
                @Override
                public void run() {
                    synchronized (this) {
                        for(int i=0;i<5;i++) {
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName() + " 正在進行打印 " +i);
                        }
                    }
                }
            }
    
            Runnable runnable = new MyRunnable();
            Thread t1 = new Thread(runnable,"t1");
            Thread t2 = new Thread(runnable,"t2");
    
            t1.start();
            t2.start();
        }
    }
    運行結果:
    t1 正在進行打印 0 t1 正在進行打印 1 t1 正在進行打印 2 t1 正在進行打印 3 t1 正在進行打印 4 t2 正在進行打印 0 t2 正在進行打印 1 t2 正在進行打印 2 t2 正在進行打印 3 t2 正在進行打印 4
    結果說明:run()方法中存在synchronized(this)代碼塊,並且t1和t2都是基於MyRunnable這個Runnable對象建立的線程。這就意味着,咱們能夠將synchronized(this)中的this看作是MyRunnable這個Runnable對象;所以,線程t1和t2共享「MyRunable對象的同步鎖」。因此,當一個線程運行的時候,另一個線程必須等待正在運行的線程釋放MyRunnable的同步鎖以後才能運行。
    • 實例2:繼承Thread類
    public class ThreadTest {
        public static void main(String[] args) {
    
            class MyThread extends Thread{
                public MyThread(String name){
                    super(name);
                }
                @Override
                public void run() {
                    synchronized(this){
                        for(int i=0;i<10;i++){
                            try {
                                Thread.sleep(100);
                                System.out.println(Thread.currentThread().getName()+" 正在進行打印 "+i);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
            Thread t1 = new MyThread("t1");
            Thread t2 = new MyThread("t2");
            t1.start();
            t2.start();
        }
    }

    運行結果:
    t2 正在進行打印 0 t1 正在進行打印 0 t2 正在進行打印 1 t1 正在進行打印 1 t1 正在進行打印 2 t2 正在進行打印 2 t2 正在進行打印 3 t1 正在進行打印 3 t1 正在進行打印 4 t2 正在進行打印 4
    對比結果:發現實例1的兩個線程是一個結束後,另外一個才運行,實例2的是交叉運行,在run()方法中都有synchronized(this),爲何結果不同?this

    分析:synchronized(this)中的this是指當前對象,即synchronized(this)所在類對應的當前對象。它的做用是獲取獲取當前對象的同步鎖。對於實例2中的synchronized(this)中的this表明的是MyThread對象,t1和t2是兩個不一樣的MyThread對象,所以t1和t2在執行synchronized(this)時獲取的是不一樣對象的同步鎖。對於實例1來講,synchronized(this)中的this表明的時候MyRunnable對象t1t2共同一個MyRunnable對象,所以,一個線程獲取了對象的同步鎖,會形成另外一個線程的等待。操作系統

4.4 synchronized方法和synchronized代碼塊

4.4.1 概述

  • synchronized方法是用synchronized修飾方法,這是一種粗粒度鎖;這個同步方法(非static方法)無需顯式指定同步監視器,同步方法的同步監視器是this,也就是調用該方法的對象。
  • synchronized代碼塊是用synchronized修飾代碼塊,這是一種細粒度鎖。線程開始執行同步代碼塊以前,必須先得到對同步監視器的鎖定,任什麼時候候只能有一個線程能夠得到對同步監視器的鎖定,當同步代碼塊執行完成後,該線程會釋放對同步監視器的鎖定。雖然Java容許使用任何對象做爲同步監視器,但同步監視器的目的就是爲了阻止兩個線程對同一個共享資源進行併發訪問,所以一般推薦使用可能被併發訪問的共享資源充當同步監視器。

4.4.2 實例

public class SnchronizedTest {

    public static void main(String[] args) {

        class Demo {
            // synchronized方法
             public synchronized void synMethod() {
                    for(int i=0; i<1000000; i++)
                        ;
                }
                
                public void synBlock() {
                    // synchronized代碼塊
                    synchronized( this ) {
                        for(int i=0; i<1000000; i++)
                            ;
                    }
                }
        }
    }
}

4.5 實例鎖和全局鎖

4.5.1 概述

  • 實例鎖:鎖在某個實例對象上。若是該類是單例,那麼該鎖也是具備全局鎖的概念。實例鎖對應的就是synchronized關鍵字。
  • 全局鎖:該鎖針對的是類,不管實例多少個對象,那麼線程都共享該鎖。全局鎖對應的就是static synchronized(或者是鎖在該類的class或者classloader對象上)。

4.5.2 實例

pulbic class Something {
    public synchronized void isSyncA(){}
    public synchronized void isSyncB(){}
    public static synchronized void cSyncA(){}
    public static synchronized void cSyncB(){}
}

假設,類Something有兩個實例(對象)分別爲x和y。分析下面4組表達式獲取鎖的狀況。

  • x.isSyncA()與x.isSyncB()
    • 不能同時訪問,由於都是訪問對象x的同步鎖
  • x.isSyncA()與y.isSyncA()
    • 能夠同時訪問,由於是訪問不一樣對象(x和y)的同步鎖
  • x.cSyncA()與y.cSyncB()
    • 不能同時訪問,由於兩個方法是靜態的,至關於用Something.cSyncA()和Something.cSyncB()訪問,是相同的對象
  • x.isSyncA()與Something.cSyncA()
    • 能夠同時訪問,由於訪問不一樣對象

5. Volatile 關鍵字

5.1 Volatile原理

  • Java語言提供了一種稍微同步機制,即volatile變量,用來確保將變量的更新操做通知其餘線程
  • 在訪問volatile變量是不會執行加鎖操做,所以也就不會從新執行線程阻塞,volatile變量是一種比synchronized關鍵字輕量級的同步機制
  • 當一個變量被volatile修飾後,不但具備可見性,並且還禁止指令重排。volatile的讀性能消耗與普通變量幾乎相同,可是寫操做就慢一些,由於它要保證本地代碼中插入許多內存屏障指令來保證處理器不發生亂序執行

6. 線程等待和喚醒

6.1 經常使用方法

  • 在Object.java中,定義了wait(),notify()和notifyAll()等接口
  • wait()方法的做用是讓當前線程進入阻塞狀態,同時會釋放當前對象所持有的鎖
  • notify()喚醒當前對象上的等待線程,notifyAll()則是喚醒全部的線程

6.2 實例

package com.ljw.thread;

public class WaitDemo {
    public static void main(String[] args) {

        class ThreadTest extends Thread{
            
            @Override
            public void run() {
                synchronized (this) {
                    System.out.println("開始運行線程 "+Thread.currentThread().getName());
                    System.out.println("喚醒線程notify()");
                    notify();
                }
            }
        }
        
        ThreadTest thread1 = new ThreadTest();
        thread1.start();
        
        synchronized (thread1) {
            try {
                System.out.println("主線程進入阻塞,釋放thread對象的同步鎖,wait()");
                thread1.wait(); // wait()是讓當前線程進入阻塞狀態,wait()是在主線程中執行,
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("主線程繼續進行");
    }
}

7. 線程讓步和休眠

7.1 線程讓步

7.1.1 概述

  • 在Java線程中,yield()方法的做用是讓步,它能讓當前線程由「運行狀態」進入到「就緒狀態」,可能讓其它同級別的線程得到執行權,但不必定,可能它本身再次由「就緒狀態」進入到「運行狀態」

7.1.2 實例

package com.ljw.thread;

public class YieldTest {

    public static void main(String[] args) {

        class ThreadA extends Thread{
            public ThreadA(String name){
                super(name);
            }
            
            @Override
            public synchronized void run() {

                for(int i=0;i<5;i++){
                    System.out.println(" "+this.getName()+" "+i);
                    
                    if(i%2 == 0){
                        Thread.yield();
                    }
                }
            }
        }
        
        ThreadA t1 = new ThreadA("t1");
        ThreadA t2 = new ThreadA("t2");
        t1.start();
        t2.start();
    }
}

運行結果(不惟一):

t1 0
 t2 0
 t1 1
 t1 2
 t2 1
 t1 3
 t2 2
 t1 4
 t2 3
 t2 4

結果說明:
線程t1在能被2整除的時候,並不必定切換到線程2。這代表,yield()方法雖然可讓線程由「運行狀態」進入到「就緒狀態」;可是,它不必定會讓其餘線程獲取CPU執行權(其餘線程進入到「運行狀態」)。即時這個「其餘線程」與當前調用yield()的線程具備相同的優先級。

7.1.3 yield()和wait()比較

  • wait()的做用是讓當前線程由「運行狀態」進入「阻塞狀態」,而yield()是讓當前線程由「運行狀態」進入「就緒狀態」
  • wait()是會讓線程釋放它所持有的對象的同步鎖,而yield()方法不會釋放對象的同步鎖。

7.2 線程休眠

7.2.1 概述

  • sleep()方法定義在Thread類中,sleep()的做用是讓當前線程休眠,即當前線程會從「遠程狀態」進入到「休眠(阻塞)狀態」
  • sleep()會指定休眠時間,線程休眠的時間會大於/等於該休眠時間
  • 在線程從新被喚醒時,它會由「阻塞狀態」變成「就緒狀態」,從而等待CPU的調度執行。

7.2.2 sleep() 和 wait()的比較

  • wait()的做用是讓當前的線程由「運行狀態」進入到「等待(阻塞)狀態」的同時,也會釋放同步鎖- 可是sleep()的做用是讓當前線程由「運行狀態」進入到「休眠(阻塞)」狀態,但不會釋放鎖。

8. 加入一個線程

8.1 概述

  • 在一個線程T上調用另外一個線程t的 join() 方法,至關於在T中加入線程t,要等t結束後(即t.isAlive爲假), join() 後面的代碼塊纔會執行。
  • 能夠在調用jion()時帶上一個超時參數(單位能夠是毫秒,或者納秒),這樣若是目標線程在這段時間到期時尚未結束的話,join()方法總能返回

9. 終止一個線程

9.1 概述

  • interrupt()並不會終止處於「運行狀態」的線程,它會將線程的中斷標記設爲true。
  • 綜合線程處於「阻塞狀態」和「運行狀態」的終止方式,比較通用的終止線程的形式以下:
@Override
public void run() {
    try {
        // 1. isInterrupted()保證,只要中斷標記爲true就終止線程。
        while (!isInterrupted()) {
            // 執行任務...
        }
    } catch (InterruptedException ie) {  
        // 2. InterruptedException異常保證,當InterruptedException異常產生時,線程被終止。
    }
}

9.2 實例

public class InterruptBlock {

    /**
     * @param args
     */
    public static void main(String[] args) {
    
        class MyThread extends Thread{
            public MyThread(String name){
                super(name);
            }
            
            @Override
            public void run() { 
                try {
                    int i=0;
                    while(!isInterrupted()){
                         Thread.sleep(100);
                         i++;
                         System.out.println(Thread.currentThread().getName()+ " ("+this.getState()+") loop "+i);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println(Thread.currentThread().getName()+ " ("+this.getState()+") catch InterruptedExecption");
                }   
            }
        }
        
        
        try {
            
            //新建
            Thread t1 = new MyThread("t1");
            System.out.println(t1.getName()+" ("+t1.getState()+" ) is new.");
            
            System.out.println("luo1:"+t1.isInterrupted());
            //啓動
            t1.start();
            System.out.println(t1.getName()+" ("+t1.getState()+" ) is started.");
            System.out.println("luo2:"+t1.isInterrupted());
            //主線程休眠300ms,而後主線程給t1發「中斷」指令
            Thread.sleep(300);
            t1.interrupt();
            System.out.println("luo3:"+t1.isInterrupted());
            System.out.println(t1.getName()+" ("+t1.getState()+" ) is interrupted.");
            
            //主線程休眠300ms,而後查看t1的狀態
            Thread.sleep(300);
            System.out.println(t1.getName()+" ("+t1.getState()+" ) is interrupted now .");
            
            
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }

}

運行結果:

t1 (NEW ) is new.
luo1:false
t1 (RUNNABLE ) is started.
luo2:false
t1 (RUNNABLE) loop 1
t1 (RUNNABLE) loop 2
luo3:true
t1 (RUNNABLE) loop 3
t1 (RUNNABLE ) is interrupted.
t1 (TERMINATED ) is interrupted now .

9.3 interrupt()和isInterrupted()的區別

  • interrupt()和isInterrupted()都可以用於檢測對象的「中斷標記」。區別是:interrupt()除了返回中斷標記以外,它還會清除中斷標記(即將中斷標記設爲false);而isInterrupted()僅僅返回中斷標記。

線程進階

1. 線程池

  • 示例
package com.ljw.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo {
 
    public static void main(String[] args) { // 主線程
        // 線程池 ---> Executors工具類(工廠類)
        /*
         * newFixedThreadPool(int threadCount) 建立固定數量的線程池
         * newCachedThreadPool() 建立動態數量的線程池
         */
        ExecutorService es = Executors.newFixedThreadPool(3);
        
        Runnable task = new MyTask();
        
        // 提交任務
        es.submit(task); 
        es.submit(task);
        
        es.shutdown(); // 關閉線程池,則表示不在接收新任務,不表明正在線程池的任務會停掉
    }   
}

class MyTask implements Runnable{

    @Override
    public void run() {
        for(int i=0;i<100;i++) {
            System.out.println(Thread.currentThread().getName()+" MyTask "+i);
        }
    }
}

2. 線程安全與鎖

2.1 重入鎖和讀寫鎖

package com.ljw.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

/**
 * ReentrantLock類,重入鎖:Lock接口的實現類,與synchronized同樣具備互斥鎖功能 lock() 和 unlock()
 * ReentrantReadWriteLock類,讀寫鎖:一種支持一寫多讀的同步鎖,讀寫分離,分別分配讀鎖和寫鎖,在讀操做遠遠高於寫操做的環境中能夠提升效率
 *      互斥規則:
 *          寫--寫:互斥,阻塞
 *          讀--寫:互斥,阻塞
 *          讀--讀:不互斥,不阻塞
 *
 */

public class LockDemo {

    public static void main(String[] args) {
        ExecutorService es = Executors.newFixedThreadPool(20);
        Student s = new Student();
//      ReentrantLock rLock = new ReentrantLock(); // 用ReenTrantLock加鎖運行時間20008ms
        ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); // 用讀寫鎖分別對讀寫任務加鎖運行時間3003ms
        ReadLock rl = rwLock.readLock();
        WriteLock wl = rwLock.writeLock();
        
        // 寫任務
        Callable<Object> writeTask = new Callable<Object>() {

            @Override
            public Object call() throws Exception {
//              rLock.lock();
                wl.lock();
                try {
                    Thread.sleep(1000);
                    s.setValue(100);
                }finally {
//                  rLock.unlock();
                    wl.unlock();
                }
                
                return null;
            }};
        
        // 讀任務
        Callable<Object> readTask = new Callable<Object>() {

            @Override
            public Object call() throws Exception {
//              rLock.lock();
                rl.lock();
                try {
                    Thread.sleep(1000);
                    s.getValue();
                }finally {
//                  rLock.unlock();
                    rl.unlock();
                }               
                return null;
            }};

        // 開始時間
        long start = System.currentTimeMillis();
        for(int i=0;i<2;i++) { // 寫任務執行 2 次
            es.submit(writeTask);
        }
        for(int i=0;i<18;i++) { // 讀任務執行 18 次
            es.submit(readTask);
        }
        
        es.shutdown(); // 中止線程池,不在接受新的任務,將現有任務所有執行完畢
        
        while(true) {
            if(es.isTerminated()) { // 當線程池中全部任務執行完畢,返回true,不然返回false
                break;
            }
        }
        
        // 執行到這裏,說明線程池中全部任務都執行完畢,能夠計算結束時間
        System.out.println(System.currentTimeMillis()-start);
    
    }
}

class Student {
    private int value;
    
    //讀
    public int getValue() {
        return value;
    }
    
    //寫
    public void setValue(int value) {
        this.value = value;
    }
}

2.2 線程安全

2.2.1 Collections工具類

  • Collections工具類中提供了多個能夠得到線程安全集合的方法
static <T> Collection<T> synchronizedCollection(Collection<T> c) 
    //返回由指定集合支持的同步(線程安全)集合。  
static <T> List<T> synchronizedList(List<T> list) 
    //返回由指定列表支持的同步(線程安全)列表。  
static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) 
    //返回由指定地圖支持的同步(線程安全)映射。  
static <K,V> NavigableMap<K,V> synchronizedNavigableMap(NavigableMap<K,V> m) 
    //返回由指定的可導航地圖支持的同步(線程安全)可導航地圖。  
static <T> NavigableSet<T> synchronizedNavigableSet(NavigableSet<T> s) 
    //返回由指定的可導航集支持的同步(線程安全)可導航集。  
static <T> Set<T> synchronizedSet(Set<T> s) 
    //返回由指定集合支持的同步(線程安全)集。  
static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m) 
    //返回由指定的排序映射支持的同步(線程安全)排序映射。  
static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s) 
     //返回由指定的排序集支持的同步(線程安全)排序集。

2.2.2 CopyOnWriteArrayList

  • 線程安全的ArrayList
  • 讀寫分離,寫加鎖,讀沒鎖,讀寫之間不互斥
  • 使用方法與ArrayList無異

2.2.3 CopyOnWriteArraySet

  • 基於 CopyOnWriteArrayList

2.2.4 ConcurrentHashMap

  • 初始容量默認爲16段(Segment),採用分段鎖設計
  • 不對整個Map加鎖,只對每一個Segment加鎖
  • 當多個對象訪問同個Segment纔會互斥
相關文章
相關標籤/搜索