日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁 > 科技  > 軟件

看完后,你再也不用怕面試問并發(fā)編程啦

來源: 責(zé)編: 時(shí)間:2023-12-21 17:11:32 238觀看
導(dǎo)讀引言為什么很多大廠喜歡問并發(fā)編程呢?因?yàn)椴l(fā)編程是開發(fā)人員的一個(gè)分水嶺。很多好幾年開發(fā)經(jīng)驗(yàn)的開發(fā)人員可能也沒有實(shí)際的并發(fā)編程經(jīng)驗(yàn),要么就是在一些沒有挑戰(zhàn)性的中臺(tái)實(shí)現(xiàn)了所謂的分布式鎖,但是沒有并發(fā)量去考驗(yàn),要么

引言

為什么很多大廠喜歡問并發(fā)編程呢?因?yàn)椴l(fā)編程是開發(fā)人員的一個(gè)分水嶺。很多好幾年開發(fā)經(jīng)驗(yàn)的開發(fā)人員可能也沒有實(shí)際的并發(fā)編程經(jīng)驗(yàn),要么就是在一些沒有挑戰(zhàn)性的中臺(tái)實(shí)現(xiàn)了所謂的分布式鎖,但是沒有并發(fā)量去考驗(yàn),要么就是笑著說其實(shí)工作中用不上,這些開發(fā)人員后面會(huì)逐漸被AI淘汰,CURD的東西花這么多錢請(qǐng)你們干嘛呢?為什么不直接請(qǐng)個(gè)便宜的應(yīng)屆生呢?鍛煉一兩年絕對(duì)不比這些開發(fā)人員差。因此,努力越過分水嶺,往架構(gòu)組件的能力出發(fā)吧。這篇文章將會(huì)是你的出發(fā)點(diǎn),這里會(huì)詳細(xì)介紹JDK 的并發(fā)包的原理及使用方法。wuu28資訊網(wǎng)——每日最新資訊28at.com

1、JUC并發(fā)編程概述

J.U.C并發(fā)包,即java.util.concurrent包,是JDK的核心工具包,是JDK1.5之后,由 Doug Lea實(shí)現(xiàn)并引入。 wuu28資訊網(wǎng)——每日最新資訊28at.com

整個(gè)java.util.concurrent包,按照功能可以大致劃分如下:wuu28資訊網(wǎng)——每日最新資訊28at.com

  • juc-locks 鎖框架
  • juc-atomic 原子類框架
  • juc-sync 同步器框架、工具類
  • juc-collections 集合框架

課程J.U.C,分析所有基于的源碼為Oracle JDK1.8 wuu28資訊網(wǎng)——每日最新資訊28at.com

2、多線程基礎(chǔ):進(jìn)程、線程

多線程概念介紹wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 進(jìn)程:我們把運(yùn)行中的程序叫做進(jìn)程(概念)。每個(gè)進(jìn)程都會(huì)占用內(nèi)存與CPU資源(動(dòng)態(tài)性)。進(jìn)程與進(jìn)程之間各自占用各自的內(nèi)存資源,互相獨(dú)立(獨(dú)立性)。
  • 線程:線程就是進(jìn)程中的一個(gè)執(zhí)行單元,負(fù)責(zé)當(dāng)前進(jìn)程中程序的執(zhí)行。一個(gè)進(jìn)程可以包含多個(gè)線程。一個(gè)進(jìn)程包含了多個(gè)線程就是多線程。多線程可以提高程序的并行運(yùn)行效率。

     線程簡述: 線程是進(jìn)程的執(zhí)行單元,用來執(zhí)行代碼。wuu28資訊網(wǎng)——每日最新資訊28at.com

為什么使用多線程?wuu28資訊網(wǎng)——每日最新資訊28at.com

多線程有什么用?這里舉例說明:wuu28資訊網(wǎng)——每日最新資訊28at.com

比如看學(xué)習(xí)視頻時(shí)候:我們?cè)诳匆曨l的同時(shí),還可以聽到聲音,還可以看到廣告以及彈幕,這里至少用到四個(gè)線程,當(dāng)其中一個(gè)線程卡死如放不了彈幕不影響播放廣告。wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

如上圖,要達(dá)到并行執(zhí)行的效果,這里就要用到多線程。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 并行: 兩個(gè)或兩個(gè)以上的事件在同一時(shí)刻發(fā)生(同時(shí)發(fā)生)
  • 并發(fā): 兩個(gè)或兩個(gè)以上的事件在一個(gè)時(shí)間段內(nèi)發(fā)生(交替執(zhí)行)

線程調(diào)度wuu28資訊網(wǎng)——每日最新資訊28at.com

計(jì)算機(jī)通常只有一個(gè)CPU時(shí),在任意時(shí)刻只能執(zhí)行一條計(jì)算機(jī)指令,每一個(gè)進(jìn)程只有獲得CPU的使用權(quán)才能執(zhí)行指令。所謂多進(jìn)程并發(fā)運(yùn)行,從宏觀上看,其實(shí)是各個(gè)進(jìn)程輪流獲得CPU的使用權(quán),分別執(zhí)行各自的任務(wù)。那么,就會(huì)有多個(gè)線程處于就緒狀態(tài)等到CPU,JVM就負(fù)責(zé)了線程的調(diào)度。JVM采用的是搶占式調(diào)度,沒有采用分時(shí)調(diào)度,因此可能造成多線程執(zhí)行結(jié)果的的隨機(jī)性。wuu28資訊網(wǎng)——每日最新資訊28at.com

說明:在單核CPU中,同一個(gè)時(shí)刻只有一個(gè)線程執(zhí)行,根據(jù)CPU時(shí)間片算法依次為每個(gè)線程服務(wù),這就叫線程調(diào)度。wuu28資訊網(wǎng)——每日最新資訊28at.com

3、多線程編程:wait()、notify()、notifyAll()

目標(biāo): 線程等待和喚醒使用。wuu28資訊網(wǎng)——每日最新資訊28at.com

介紹wuu28資訊網(wǎng)——每日最新資訊28at.com

等待和喚醒:通常是兩個(gè)線程之間的事情一個(gè)線程等待另外一個(gè)線程負(fù)責(zé)喚醒wuu28資訊網(wǎng)——每日最新資訊28at.com

等待和喚醒wuu28資訊網(wǎng)——每日最新資訊28at.com

  • wait() 等待
  • notity() 喚醒單個(gè)
  • notityAll() 喚醒全部

Object類:wuu28資訊網(wǎng)——每日最新資訊28at.com

public final void wait();             // 導(dǎo)致當(dāng)前線程等待public final native void wait(long timeout) throws InterruptedException;public final native void notify();    // 喚醒正在等待的單個(gè)線程public final native void notifyAll(); // 喚醒正在等待的全部線程

注意:wait和notify必須是在同步代碼塊中,使用鎖對(duì)象調(diào)用wuu28資訊網(wǎng)——每日最新資訊28at.com

  • wait()方法的作用?

     使當(dāng)前線程阻塞wuu28資訊網(wǎng)——每日最新資訊28at.com

  • notify()方法的作用?

     喚醒正在等待的單個(gè)線程wuu28資訊網(wǎng)——每日最新資訊28at.com

  • notifyAll()方法的作用?

     喚醒所有等待(對(duì)象的)線程,哪一個(gè)線程將會(huì)第一個(gè)處理取決于操作系統(tǒng)的實(shí)現(xiàn)。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 為什么wait和notify方法放在Object?

     因?yàn)閣ait和notify需要使用鎖對(duì)象來調(diào)用,而任何對(duì)象都可以作為鎖,所以放在Object類中。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 詳細(xì)介紹

1、wait()、notify()、notifyAll() 方法是Object的本地final方法,子類無法被重寫。wuu28資訊網(wǎng)——每日最新資訊28at.com

2、wait() 使當(dāng)前線程阻塞,前提是必須先獲得鎖,一般配合synchronized 關(guān)鍵字使用。   wuu28資訊網(wǎng)——每日最新資訊28at.com

即,一般在 synchronized 同步代碼塊里使用 wait()、notify、notifyAll() 方法。wuu28資訊網(wǎng)——每日最新資訊28at.com

3、由于 wait()、notify()、notifyAll() 方法在 synchronized 代碼塊執(zhí)行,說明當(dāng)前線程一定是獲取了鎖的。當(dāng)線程執(zhí)行wait()方法時(shí)候,會(huì)釋放當(dāng)前的鎖,然后讓出CPU,進(jìn)入等待狀態(tài)。只有當(dāng) notify()/notifyAll() 被執(zhí)行時(shí)候,才會(huì)喚醒一個(gè)或多個(gè)正處于等待狀態(tài)的線程,然后繼續(xù)往下執(zhí)行,直到執(zhí)行完 synchronized 代碼塊的代碼或是中途遇到wait(),再次釋放鎖。也就是說,notify()/notifyAll() 的執(zhí)行只是喚醒沉睡的線程,而不會(huì)立即釋放鎖,鎖的釋放要看代碼塊的具體執(zhí)行情況。所以在編程中,盡量在使用了 notify()/notifyAll() 后立即退出臨界區(qū),以喚醒其他線程讓其獲得鎖。wuu28資訊網(wǎng)——每日最新資訊28at.com

4、notify 和 wait 的順序不能錯(cuò),如果A線程先執(zhí)行notify方法,B線程在執(zhí)行wait方法,那么B線程是無法被喚醒的。wuu28資訊網(wǎng)——每日最新資訊28at.com

5、notify 和 notifyAll的區(qū)別:   wuu28資訊網(wǎng)——每日最新資訊28at.com

  • notify: 只喚醒一個(gè)等待(對(duì)象的)線程并使該線程開始執(zhí)行。所以如果有多個(gè)線程等待一個(gè)對(duì)象,這個(gè)方法只會(huì)喚醒其中一個(gè)線程,選擇哪個(gè)線程取決于操作系統(tǒng)對(duì)多線程管理的實(shí)現(xiàn)。      
  • notifyAll: 會(huì)喚醒所有等待(對(duì)象的)線程,盡管哪一個(gè)線程將會(huì)第一個(gè)處理取決于操作系統(tǒng)的實(shí)現(xiàn)。如果當(dāng)前情況下有多個(gè)線程需要被喚醒,推薦使用notifyAll方法。

代碼:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;import java.util.concurrent.TimeUnit;/** * 測試 wait()、notify()、notifyAll() */public class Test1 { public static void main(String[] args) { // 創(chuàng)建對(duì)象 Object obj = new Object(); // 線程t1 new Thread(() -> { synchronized (obj) { try { System.out.println(Thread.currentThread().getName() + "wait 前"); obj.wait(); // 等待、線程阻塞(釋放鎖) System.out.println(Thread.currentThread().getName() + "wait 后"); } catch (InterruptedException e) { e.printStackTrace(); } } }, "t1:").start(); // 線程t2 new Thread(() -> { synchronized (obj) { try { System.out.println(Thread.currentThread().getName() + "wait 前"); obj.wait(); // 等待、線程阻塞(釋放鎖) System.out.println(Thread.currentThread().getName() + "wait 后"); } catch (InterruptedException e) { e.printStackTrace(); } } }, "t2:").start(); // 線程t3 new Thread(() -> { synchronized (obj) { try { // 休眠2秒 TimeUnit.SECONDS.sleep(2); //目的讓前2個(gè)線程進(jìn)入等待狀態(tài) } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("notifyAll 前"); obj.notifyAll(); // 喚醒全部等待的線程,不會(huì)釋放鎖 System.out.println("notifyAll 后"); } }, "t3:").start(); }

小結(jié)wuu28資訊網(wǎng)——每日最新資訊28at.com

  • wait()方法的作用?

導(dǎo)致當(dāng)前線程等待。(釋放鎖,讓出CPU)wuu28資訊網(wǎng)——每日最新資訊28at.com

  • notify()方法的作用?

喚醒正在等待的單個(gè)線程。(不釋放鎖,不讓出CPU)wuu28資訊網(wǎng)——每日最新資訊28at.com

4、多線程編程:線程狀態(tài)及狀態(tài)轉(zhuǎn)換

目標(biāo)理解線程6種狀態(tài)以及狀態(tài)轉(zhuǎn)換。wuu28資訊網(wǎng)——每日最新資訊28at.com

線程狀態(tài)wuu28資訊網(wǎng)——每日最新資訊28at.com

線程可以處于以下狀態(tài)之一:wuu28資訊網(wǎng)——每日最新資訊28at.com

  • NEW 尚未啟動(dòng)的線程處于此狀態(tài)。
  • RUNNABLE 在Java虛擬機(jī)中執(zhí)行的線程處于此狀態(tài)。
  • BLOCKED 被阻塞等待的線程處于此狀態(tài)。
  • WAITING 無限等待另一個(gè)線程執(zhí)行特定動(dòng)作的線程處于此狀態(tài)。
  • TIMED_WAITING 一個(gè)正在限時(shí)等待另一個(gè)線程執(zhí)行一個(gè)動(dòng)作的線程處于這一狀態(tài)。
  • TERMINATED 已退出的線程處于此狀態(tài)。

     Thread.State 枚舉類中進(jìn)行了定義。wuu28資訊網(wǎng)——每日最新資訊28at.com

狀態(tài)轉(zhuǎn)換wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

代碼:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;/** 測試: 線程6種狀態(tài) */public class Test2 { // 方法1 (RUNNABLE: 運(yùn)行) public static void test1() { new Thread(() -> { synchronized (Test2.class) { while (true) { } } }, "t1-runnable").start(); //線程運(yùn)行中 } // 方法2 (TIMED_WAITING : 超時(shí)等待) public static void test2() { new Thread(() -> { synchronized (Test2.class) { while (true) { try { Test2.class.wait(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }, "t2-timed_waiting").start(); //線程超時(shí)等待中 } // 方法3 public static void test3() { // (RUNNABLE: 運(yùn)行) new Thread(() -> { synchronized (Test2.class) { while (true) { } } }, "t3-runnable").start(); // (BLOCKED: 阻塞) new Thread(() -> { synchronized (Test2.class) { //由于上面沒有釋放鎖,被阻塞中 } }, "t4-BLOCKED").start(); } // 方法4 (WAITING : 等待) public static void test4() { new Thread(() -> { synchronized (Test2.class) { while (true) { try { Test2.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } }, "t5-waiting").start(); //線程等待中 } public static void main(String[] args) { //test1(); // 線程運(yùn)行中 //test2(); // 線程超時(shí)等待中 test3(); // 線程阻塞中 //test4(); // 線程等待中 }}

查看進(jìn)程堆棧wuu28資訊網(wǎng)——每日最新資訊28at.com

使用jstack可查看指定進(jìn)程(pid)的堆棧信息,用以分析線程執(zhí)行狀態(tài):wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 進(jìn)入cmd: Win + R

wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 輸入jstack 進(jìn)程號(hào)

wuu28資訊網(wǎng)——每日最新資訊28at.com

5、多線程編程:join、yield、sleep區(qū)別?

目標(biāo): 學(xué)習(xí)如何控制線程執(zhí)行順序、線程讓步、優(yōu)先級(jí)。wuu28資訊網(wǎng)——每日最新資訊28at.com

join 作用wuu28資訊網(wǎng)——每日最新資訊28at.com

  • join()方法【加入線程】,把指定的線程加入到當(dāng)前線程,可以將兩個(gè)交替執(zhí)行的線程合并為順序執(zhí)行的線程。線程調(diào)用了join方法,那么就要一直運(yùn)行到該線程結(jié)束,才會(huì)運(yùn)行其他線程。這樣可以控制線程執(zhí)行順序。
  • join()方法,內(nèi)部實(shí)現(xiàn)使用了 synchronized 會(huì)占用鎖。線程結(jié)束,鎖釋放。
  •  join(long millis)

     – 如果為0表示永遠(yuǎn)等待,其實(shí)是等到線程結(jié)束后。wuu28資訊網(wǎng)——每日最新資訊28at.com

     – 傳入指定的時(shí)間會(huì)調(diào)用wait(millis), 時(shí)間到鎖釋放,不再等待。wuu28資訊網(wǎng)——每日最新資訊28at.com

yield 作用wuu28資訊網(wǎng)——每日最新資訊28at.com

  • thread.yield()【線程讓步】 讓出CPU的時(shí)間片盡量切換到其它線程去執(zhí)行。
  • 使正在運(yùn)行中的線程重新變成就緒狀態(tài),并重新競爭 CPU 的調(diào)度權(quán)。它可能會(huì)獲取到,也有可能被其它線程獲取到。

yield 和 sleep 的異同wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 優(yōu)先級(jí):sleep休眠線程后,會(huì)給其他線程執(zhí)行機(jī)會(huì),不考慮線程的優(yōu)先級(jí)問題;yield讓步后只有優(yōu)先級(jí)高于或等于當(dāng)前線程的線程才有執(zhí)行機(jī)會(huì)。
  • 狀態(tài):sleep當(dāng)前線程由運(yùn)行態(tài)進(jìn)入超時(shí)等待狀態(tài);yield當(dāng)前線程由運(yùn)行態(tài)到就緒態(tài)。
  • 異常:sleep方法在聲明時(shí)拋出InterruptedException異常,所以在使用時(shí)要么try捕獲要么throws拋出;而yield沒有聲明異常。

線程優(yōu)先級(jí)wuu28資訊網(wǎng)——每日最新資訊28at.com

線程的優(yōu)先級(jí)說明該線程在程序中的重要性。系統(tǒng)會(huì)根據(jù)優(yōu)先級(jí)決定首先使用哪個(gè)線程,但這并不意味著優(yōu)先級(jí)低的線程得不到運(yùn)行,只是它運(yùn)行的機(jī)率比較小而已,比如垃圾回收機(jī)制。wuu28資訊網(wǎng)——每日最新資訊28at.com

優(yōu)先級(jí)范圍1-10,默認(rèn)為5,比如設(shè)置最高優(yōu)先級(jí)為10:wuu28資訊網(wǎng)——每日最新資訊28at.com

t1.setPriority(Thread.MAX_PRIORITY);wuu28資訊網(wǎng)——每日最新資訊28at.com

代碼:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;/** * join() : 加入線程 * yield() : 線程讓步 * sleep() : 線程休眠 */public class Test3 {    public static void main(String[] args) throws InterruptedException {        // 線程1        Thread t1 = new Thread(() -> {            for (int i = 1; i <= 20; i++) {                System.out.println(Thread.currentThread().getName() + i);                // 線程讓步                //Thread.yield();            }        },"t1:");        // 線程2        Thread t2 = new Thread(() -> {            for (int i = 1; i <= 20; i++) {                System.out.println(Thread.currentThread().getName() + i);            }        },"t2:");        // 啟動(dòng)線程        t1.start();        // 加入線程        t1.join();        // 設(shè)置線程優(yōu)先級(jí)        //t1.setPriority(Thread.MIN_PRIORITY);        t2.start();        // 設(shè)置線程優(yōu)先級(jí)        //t2.setPriority(Thread.MIN_PRIORITY);    }}

小結(jié)wuu28資訊網(wǎng)——每日最新資訊28at.com

  • join作用?

 加入線程,線程調(diào)用了join方法,那么就要一直運(yùn)行到該線程結(jié)束,才會(huì)運(yùn)行其他線程. 這樣可以控制線程執(zhí)行順序。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • yield作用?

 線程讓步,讓出CPU的時(shí)間片盡量切換其他線程去執(zhí)行。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 線程優(yōu)先級(jí)?

     設(shè)置線程優(yōu)先級(jí),優(yōu)先級(jí)可以設(shè)置1-10,數(shù)字越大代表優(yōu)先級(jí)越高。wuu28資訊網(wǎng)——每日最新資訊28at.com

     在Java語言中,每個(gè)線程都有一個(gè)優(yōu)先級(jí),當(dāng)線程調(diào)控器有機(jī)會(huì)選擇新的線程時(shí),線程的優(yōu)先級(jí)越高越有可能先被選擇執(zhí)行。wuu28資訊網(wǎng)——每日最新資訊28at.com

6、并發(fā)編程需要處理的問題:死鎖問題

并發(fā)編程的目的是為了讓程序運(yùn)行得更快,但是,并不是啟動(dòng)更多的線程就能讓程序最大限度地并發(fā)執(zhí)行。在進(jìn)行并發(fā)編程時(shí),如果希望通過多線程執(zhí)行任務(wù)讓程序運(yùn)行得更快,會(huì)面臨非常多的挑戰(zhàn),比如死鎖的問題、上下文切換的問題。wuu28資訊網(wǎng)——每日最新資訊28at.com

描述wuu28資訊網(wǎng)——每日最新資訊28at.com

鎖是個(gè)非常有用的工具,運(yùn)用場景非常多,因?yàn)樗褂闷饋矸浅:唵危乙子诶斫狻5瑫r(shí)它也會(huì)帶來一些困擾,那就是可能會(huì)引起死鎖,一旦產(chǎn)生死鎖,就會(huì)造成系統(tǒng)功能不可用。讓我們先來看一段代碼,這段代碼會(huì)引起死鎖,使線程t1 和 線程t2 互相等待對(duì)方釋放鎖。wuu28資訊網(wǎng)——每日最新資訊28at.com

演示wuu28資訊網(wǎng)——每日最新資訊28at.com

么是死鎖?多線程競爭共享資源,導(dǎo)致線程相互等待,程序無法向下執(zhí)行。wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

執(zhí)行sleep的時(shí)候會(huì)讓出CPU,但不是釋放鎖。 wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;/** 學(xué)習(xí)死鎖的概念和解決死鎖 */public class DeadLock { // 定義兩個(gè)對(duì)象作為鎖 private static Object objA = new Object(); private static Object objB = new Object(); public static void main(String[] args) { // 線程1 Thread t1 = new Thread(() -> { // 同步鎖 synchronized (objA){ try { // 線程休眠(讓出CPU,不釋放鎖) Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("AAAAAAA"); // 同步鎖 synchronized (objB){ System.out.println("BBBBBBB"); } } }); // 線程2 Thread t2 = new Thread(() -> { // 同步鎖 synchronized (objB){ System.out.println("CCCCCCC"); // 同步鎖 synchronized (objA){ System.out.println("DDDDDDD"); } } }); t1.start(); t2.start(); }}

上面的代碼只是演示死鎖的場景,在現(xiàn)實(shí)中你可能不會(huì)寫出這樣的代碼。但是,在一些更為復(fù)雜的場景中,你可能會(huì)遇到這樣的問題,比如t1拿到鎖之后,因?yàn)橐恍┊惓G闆r沒有釋放鎖(死循環(huán))。又或者是t1拿到一個(gè)數(shù)據(jù)庫鎖,釋放鎖的時(shí)候拋出了異常,沒釋放掉。一旦出現(xiàn)死鎖,業(yè)務(wù)是可感知的,因?yàn)椴荒芾^續(xù)提供服務(wù)了。wuu28資訊網(wǎng)——每日最新資訊28at.com

查看線程執(zhí)行情況wuu28資訊網(wǎng)——每日最新資訊28at.com

打開cmd命令dos窗口輸入如下命令wuu28資訊網(wǎng)——每日最新資訊28at.com

jps命令:查看Java程序進(jìn)程id信息wuu28資訊網(wǎng)——每日最新資訊28at.com

jstack命令:查看指定進(jìn)程堆棧信息wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

如何避免死鎖?wuu28資訊網(wǎng)——每日最新資訊28at.com

現(xiàn)在我們介紹避免死鎖的幾個(gè)常見方法:wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 避免一個(gè)線程同時(shí)獲取多個(gè)鎖。
  • 避免一個(gè)線程在鎖內(nèi)同時(shí)占用多個(gè)資源,盡量保證每個(gè)鎖只占用一個(gè)資源。
  • 嘗試使用定時(shí)鎖,使用lock.tryLock(timeout)來替代使用內(nèi)部鎖機(jī)制。

小結(jié)wuu28資訊網(wǎng)——每日最新資訊28at.com

1.什么是死鎖: 多線程競爭共享資源,導(dǎo)致線程相互等待,程序無法向下執(zhí)行。wuu28資訊網(wǎng)——每日最新資訊28at.com

2.死鎖產(chǎn)生的條件wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 有多個(gè)線程   
  • 有多把鎖   
  • 有同步代碼塊嵌套

3.如何避免死鎖: 干掉其死鎖產(chǎn)生的條件中一個(gè)條件即可。wuu28資訊網(wǎng)——每日最新資訊28at.com

7、并發(fā)編程需要處理的問題:上下文過度切換

多線程一定快嗎?wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

測試代碼wuu28資訊網(wǎng)——每日最新資訊28at.com

代碼演示串行和并發(fā)執(zhí)行并累加操作的時(shí)間,請(qǐng)分析: 下面的代碼并發(fā)執(zhí)行一定比串行執(zhí)行快嗎?wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;public class Test4 {    // 定義變量    private static final long count = 1000000000;    public static void main(String[] args) throws InterruptedException {        concurrency();        serial();    }    // 定義方法1(使用線程)    private static void concurrency() throws InterruptedException {        long start = System.currentTimeMillis();        // 創(chuàng)建線程 循環(huán)累加        Thread thread = new Thread(() -> {            int a = 0;            for (long i = 0; i < count; i++) {                a += 5;            }        });        // 開啟線程        thread.start();        // 循環(huán)累減        int b = 0;        for (long i = 0; i < count; i++) {            b--;        }        long time = System.currentTimeMillis() - start;       // thread.join();        System.out.println("concurrency :" + time + "ms,b=" + b);    }    // 定義方法2 (不用線程)    private static void serial() {        long start = System.currentTimeMillis();        // 循環(huán)累加        int a = 0;        for (long i = 0; i < count; i++) {            a += 5;        }        // 循環(huán)累減        int b = 0;        for (long i = 0; i < count; i++) {            b--;        }        long time = System.currentTimeMillis() - start;        System.out.println("serial:" + time + "ms,b=" + b );    }}

測試結(jié)果wuu28資訊網(wǎng)——每日最新資訊28at.com

上述問題的答案是“不一定”,測試結(jié)果如表所示:wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

當(dāng)并發(fā)執(zhí)行累加操作不超過百萬次時(shí),速度會(huì)比串行執(zhí)行累加操作要慢。那么,為什么并發(fā)執(zhí)行的速度會(huì)比串行慢呢?這是因?yàn)榫€程有創(chuàng)建和上下文切換的開銷。wuu28資訊網(wǎng)——每日最新資訊28at.com

上下文切換wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 即使是單核處理器也支持多線程執(zhí)行代碼,CPU 通過給每個(gè)線程分配CPU 時(shí)間片來實(shí)現(xiàn)這個(gè)機(jī)制。時(shí)間片是CPU 分配給各個(gè)線程的時(shí)間,因?yàn)闀r(shí)間片非常短,所以CPU 通過不停地切換線程執(zhí)行,讓我們感覺多個(gè)線程是同時(shí)執(zhí)行的,時(shí)間片一般是幾十毫秒(ms)。
  • CPU 通過時(shí)間片分配算法來循環(huán)執(zhí)行任務(wù),當(dāng)前任務(wù)執(zhí)行一個(gè)時(shí)間片后會(huì)切換到下一個(gè)任務(wù)。但是,在切換前會(huì)保存上一個(gè)任務(wù)的狀態(tài),以便下次切換回這個(gè)任務(wù)時(shí),可以再加載這個(gè)任務(wù)的狀態(tài)。所以任務(wù)從保存到再加載的過程就是一次上下文切換。

例如:這就像我們同時(shí)讀兩本書,當(dāng)我們?cè)谧x一本英文的技術(shù)書時(shí),發(fā)現(xiàn)某個(gè)單詞不認(rèn)識(shí),于是便打開中英文字典,但是在放下英文技術(shù)書之前,大腦必須先記住這本書讀到了多少頁的第多少行,等查完單詞之后,能夠繼續(xù)讀這本書。這樣的切換是會(huì)影響讀書效率的,同樣上下文切換也會(huì)影響多線程的執(zhí)行速度。wuu28資訊網(wǎng)——每日最新資訊28at.com

如何減少上下文切換wuu28資訊網(wǎng)——每日最新資訊28at.com

減少上下文切換的方法有無鎖并發(fā)編程、CAS算法、使用最少線程和使用協(xié)程。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 無鎖并發(fā)編程: 多線程競爭鎖時(shí),會(huì)引起上下文切換,所以多線程處理數(shù)據(jù)時(shí),可以用一些辦法來避免使用鎖,如將數(shù)據(jù)的ID按照Hash 算法取模分段,不同的線程處理不同段的數(shù)據(jù)。
  • CAS算法:Java的Atomic包使用CAS算法來更新數(shù)據(jù),而不需要加鎖。
  • 使用最少線程:避免創(chuàng)建不需要的線程,比如任務(wù)很少,但是創(chuàng)建了很多線程來處理,這樣會(huì)造成大量線程都處于等待狀態(tài)。
  • 協(xié)程:在單線程里實(shí)現(xiàn)多任務(wù)的調(diào)度,并在單線程里維持多個(gè)任務(wù)間的切換。

小結(jié)wuu28資訊網(wǎng)——每日最新資訊28at.com

強(qiáng)烈建議多使用JDK并發(fā)包提供的并發(fā)原子類和工具類來解決并發(fā)問題,因?yàn)檫@些類都已經(jīng)通過了充分的測試和優(yōu)化,均可解決了上面提到的幾個(gè)挑戰(zhàn)。wuu28資訊網(wǎng)——每日最新資訊28at.com

8、Java內(nèi)存模型(Java Memory Model)

什么是JMM內(nèi)存模型?wuu28資訊網(wǎng)——每日最新資訊28at.com

Java內(nèi)存模型(即Java Memory Model,簡稱JMM)。Java內(nèi)存模型跟CPU緩存模型類似,是基于CPU緩存模型來建立的,Java內(nèi)存模型是標(biāo)準(zhǔn)化的,屏蔽了底層不同計(jì)算機(jī)的區(qū)別。wuu28資訊網(wǎng)——每日最新資訊28at.com

JMM本身是一種抽象的概念,并不真實(shí)存在,它描述的是一組規(guī)則或規(guī)范,通過這組規(guī)范定義了程序中各個(gè)變量(包括實(shí)例字段,靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素)的訪問方式。由于JVM運(yùn)行程序的實(shí)體是線程,而每個(gè)線程創(chuàng)建時(shí)JVM都會(huì)為其創(chuàng)建一個(gè)工作內(nèi)存(有些地方稱為??臻g),用于存儲(chǔ)線程私有的數(shù)據(jù)。而Java內(nèi)存模型中規(guī)定所有共享變量都存儲(chǔ)在主內(nèi)存,主內(nèi)存是共享內(nèi)存區(qū)域,所有線程都可以訪問。wuu28資訊網(wǎng)——每日最新資訊28at.com

線程對(duì)共享變量的操作(讀取賦值等)必須在工作內(nèi)存中進(jìn)行,首先要將變量從主內(nèi)存拷貝的自己的工作內(nèi)存空間,然后對(duì)變量進(jìn)行操作,操作完成后再將變量寫回主內(nèi)存,不能直接操作主內(nèi)存中的變量,工作內(nèi)存中存儲(chǔ)著主內(nèi)存中的變量副本拷貝,前面說過,工作內(nèi)存是每個(gè)線程的私有數(shù)據(jù)區(qū)域,因此不同的線程間無法訪問對(duì)方的工作內(nèi)存,線程間的通信(傳值)必須通過主內(nèi)存來完成,其簡要訪問過程如下圖:wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

關(guān)于JMM中的主內(nèi)存和工作內(nèi)存說明如下:wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 主內(nèi)存

要存儲(chǔ)的是Java實(shí)例對(duì)象,所有線程創(chuàng)建的實(shí)例對(duì)象都存放在主內(nèi)存中,不管該實(shí)例對(duì)象是成員變量還是方法中的本地變量(也稱局部變量),當(dāng)然也包括了共享的類信息、常量、靜態(tài)變量。由于是共享數(shù)據(jù)區(qū)域,多條線程對(duì)同一個(gè)變量進(jìn)行訪問可能會(huì)發(fā)現(xiàn)線程安全問題。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 工作內(nèi)存

主要存儲(chǔ)當(dāng)前方法的所有本地變量信息(工作內(nèi)存中存儲(chǔ)著主內(nèi)存中的變量副本拷貝),每個(gè)線程只能訪問自己的工作內(nèi)存,即線程中的本地變量對(duì)其它線程是不可見的,就算是兩個(gè)線程執(zhí)行的是同一段代碼,它們也會(huì)各自在自己的工作內(nèi)存中創(chuàng)建屬于當(dāng)前線程的本地變量,當(dāng)然也包括了字節(jié)碼行號(hào)指示器、相關(guān)Native方法的信息。注意由于工作內(nèi)存是每個(gè)線程的私有數(shù)據(jù),線程間無法相互訪問工作內(nèi)存,因此存儲(chǔ)在工作內(nèi)存的數(shù)據(jù)不存在線程安全問題。wuu28資訊網(wǎng)——每日最新資訊28at.com

啟動(dòng)2個(gè)線程,線程A讀取主內(nèi)存的共享變量數(shù)據(jù),之后線程B修改共享變量數(shù)據(jù),線程A無法感知:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;public class Test5 {    // 定義flag屬性    private static boolean flag = false;    public static void main(String[] args) throws InterruptedException {        // 創(chuàng)建線程1        new Thread(() -> {            long num = 0;            while (!flag){                num++;            }            // 如果沒有打印,說明當(dāng)前線程無法感知flag的修改            System.out.println("num = " + num);        }).start();        // 休眠1000毫秒        Thread.sleep(1000);        // 創(chuàng)建線程2        new Thread(() -> {            // 修改flag            flag = true;            System.out.println("flag = " + flag);        }).start();    }}

運(yùn)行效果:沒有任務(wù)打印輸出,上面的線程無法感知flag的修改。wuu28資訊網(wǎng)——每日最新資訊28at.com

9、Java并發(fā)編程三大特性

Java并發(fā)編程三個(gè)特性: 可見性、原子性、有序性。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 可見性:一個(gè)線程修改了某個(gè)共享變量,其狀態(tài)能夠立即被其他線程知曉,通常被解釋為將線程本地狀態(tài)反映到主內(nèi)存上,volatile就是負(fù)責(zé)保證可見性的。
  • 原子性:相關(guān)操作不會(huì)中途被其他線程干擾,一般通過同步機(jī)制實(shí)現(xiàn)。
  • 有序性:保證線程內(nèi)串行語義,避免指令重排。

9.1 可見性

可見性表示的是,如果有線程更新了某一個(gè)共享變量的值,則其它線程要能夠立即感知到最新的內(nèi)容。如果不能保證可見性,則可能出現(xiàn)類似于數(shù)據(jù)庫中的臟讀情況。wuu28資訊網(wǎng)——每日最新資訊28at.com

前文介紹JMM的時(shí)候也提到了,如果要保證可見性,那么變量被一個(gè)線程修改后,需要將其修改后的最新值同步回主存,然后其它線程要讀取該變量時(shí),需要從主存刷新最新的值到本地內(nèi)存,就這樣通過主存實(shí)現(xiàn)可見性。但是將最新值同步回主存的時(shí)機(jī)是沒有強(qiáng)制要求的,也不知道其它線程什么時(shí)候可能會(huì)去從主存刷新最新值,所以普通變量在多線程操作時(shí)是保證不了可見性的。wuu28資訊網(wǎng)——每日最新資訊28at.com

這時(shí)有一個(gè)比較好使的關(guān)鍵字:volatile。JMM對(duì)它定義了一些特殊的訪問規(guī)則,它能保證修改后的最新值能立即同步到主存,同時(shí),每次使用都從主存刷新。所以volatile能夠保證多線程場景下的可見性。wuu28資訊網(wǎng)——每日最新資訊28at.com

volatile 介紹wuu28資訊網(wǎng)——每日最新資訊28at.com

在多線程并發(fā)編程中synchronized和volatile都扮演著重要的角色,volatile是輕量級(jí)的synchronized,它在多處理器開發(fā)中保證了共享變量的“可見性”??梢娦缘囊馑际钱?dāng)一個(gè)線程修改一個(gè)共享變量時(shí),另外一個(gè)線程能讀到這個(gè)修改的值。如果volatile變量修飾符使用恰當(dāng)?shù)脑?,它比synchronized的使用和執(zhí)行成本更低,因?yàn)樗粫?huì)引起線程上下文的切換和調(diào)度。wuu28資訊網(wǎng)——每日最新資訊28at.com

volatile 使用wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;/** volatile 實(shí)現(xiàn)多線程訪問共享成員時(shí)的可見性 */public class Test6 { // volatile 實(shí)現(xiàn)多線程訪問共享成員時(shí)的可見性. private static volatile boolean flag = false; public static void main(String[] args) throws InterruptedException { // 創(chuàng)建線程1 new Thread(() -> { long num = 0; while (!flag){ num++; } // 如果沒有打印,說明當(dāng)前線程無法感知flag的修改 System.out.println("num = " + num); }).start(); // 休眠1000毫秒 Thread.sleep(1000); // 創(chuàng)建線程2 new Thread(() -> { // 修改flag flag = true; System.out.println("flag = " + flag); }).start(); }}

9.2 原子性

在計(jì)算機(jī)中,它表示的是一個(gè)操作,可能包含一個(gè)或多個(gè)步驟,這些步驟要么全部執(zhí)行成功要么全部執(zhí)行失敗,并且執(zhí)行的過程中不能被其它操作打斷,這類似于數(shù)據(jù)庫中事務(wù)的原子性概念。wuu28資訊網(wǎng)——每日最新資訊28at.com

數(shù)據(jù)原子操作,Java 內(nèi)存模型對(duì)主內(nèi)存與工作內(nèi)存之間的具體交互協(xié)議定義了八種原子操作:wuu28資訊網(wǎng)——每日最新資訊28at.com

1. lock(鎖定): 作用于主內(nèi)存的變量,把一個(gè)變量標(biāo)記為一條線程獨(dú)占狀態(tài)。

2. unlock(解鎖): 作用于主內(nèi)存的變量,把一個(gè)處于鎖定狀態(tài)的變量釋放出來,釋放后的變量才可以被其他線程鎖定。

3. read(讀取): 作用于主內(nèi)存的變量,把一個(gè)變量值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存中,以便隨后的load動(dòng)作使用。

4. load(載入): 作用于工作內(nèi)存的變量,它把read操作從主內(nèi)存中得到的變量值放入工作內(nèi)存的變量副本中。

5. use(使用): 作用于工作內(nèi)存的變量,把工作內(nèi)存中的一個(gè)變量值傳遞給執(zhí)行引擎。

6. assign(賦值): 作用于工作內(nèi)存的變量,它把一個(gè)從執(zhí)行引擎接收到的值賦給工作內(nèi)存的變量。

7. store(存儲(chǔ)): 作用于工作內(nèi)存的變量,把工作內(nèi)存中的一個(gè)變量的值傳送到主內(nèi)存中,以便隨后的write的操作。

8. write(寫入): 作用于工作內(nèi)存的變量,它把store操作從工作內(nèi)存中的一個(gè)變量的值傳送到主內(nèi)存的變量中。wuu28資訊網(wǎng)——每日最新資訊28at.com

如果要把一個(gè)變量從主內(nèi)存中復(fù)制到工作內(nèi)存中,就需要按順序地執(zhí)行read和load操作,如果把變量從工作內(nèi)存中同步到主內(nèi)存中,就需要按順序地執(zhí)行store和write操作。但Java內(nèi)存模型只要求上述操作必須按順序執(zhí)行,而沒有保證必須是連續(xù)執(zhí)行。wuu28資訊網(wǎng)——每日最新資訊28at.com

對(duì)應(yīng)如下的流程圖:wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

比如:i = i + 1,就是一個(gè)非原子操作,它涉及到獲取i,獲取1,相加,賦值等4個(gè)操作,所以在多線程情況下可能會(huì)出現(xiàn)并發(fā)問題。wuu28資訊網(wǎng)——每日最新資訊28at.com

比如 i = i+1,我們要保證它的原子性 該怎么做呢?可以通過八種操作中的lock和unlock來達(dá)到目的。但是JVM并沒有把lock和unlock操作直接開放給用戶使用,我們的Java代碼中,就是大家所熟知的synchronized關(guān)鍵字保證原子性。wuu28資訊網(wǎng)——每日最新資訊28at.com

代碼:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;public class Test7 { // 無法在多線程的情況下實(shí)現(xiàn)原子自遞增的問題。 private static int count = 0; // 定義累計(jì)的方法 public synchronized static void inc(){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } count++; } public static void main(String[] args) throws InterruptedException { // 循環(huán)創(chuàng)建線程 for(int i = 0; i < 1000; i++){ new Thread(() -> { inc(); }).start(); } Thread.sleep(4000); System.out.println("y運(yùn)行結(jié)果:"+count); }}

9.3 有序性

Java內(nèi)存模型中,允許編輯器和處理器對(duì)指令進(jìn)行重排序,但是重排序過程不會(huì)影響到單線程程序的執(zhí)行,卻會(huì)影響到多線程并發(fā)執(zhí)行的正確性。wuu28資訊網(wǎng)——每日最新資訊28at.com

從字面上的意思理解,有序就是要保證代碼按照既定的順序依次執(zhí)行。但是CPU(或編譯器)出于性能優(yōu)化的目的,在保證不會(huì)對(duì)程序運(yùn)行結(jié)果產(chǎn)生影響的前提下,代碼的執(zhí)行順序可能會(huì)和我們既定的順序不一致。wuu28資訊網(wǎng)——每日最新資訊28at.com

示例wuu28資訊網(wǎng)——每日最新資訊28at.com

int i = 1;
int j = 1;wuu28資訊網(wǎng)——每日最新資訊28at.com

這兩行代碼互相沒有任何依賴關(guān)系,誰先執(zhí)行還是后執(zhí)行,對(duì)程序運(yùn)行結(jié)果都不會(huì)有什么影響。經(jīng)過指令重排后,可能 int j = 1; 就比int i = 1;先執(zhí)行了。不同的CPU架構(gòu)可能支持不同的重排規(guī)則,像Load-Load、Load-Store、Store-Store、Store-Load等等。wuu28資訊網(wǎng)——每日最新資訊28at.com

指令重排的后果在并發(fā)的情況下有時(shí)會(huì)是嚴(yán)重的,比如以下代碼:wuu28資訊網(wǎng)——每日最新資訊28at.com

public void execute(){ int a = 0; int b = 1; int c = a + b;}

這里a=0,b=1兩句可以隨便排序,不影響程序邏輯結(jié)果,但c=a+b這句必須在前兩句的后面執(zhí)行。 wuu28資訊網(wǎng)——每日最新資訊28at.com

從前面那個(gè)例子可以看到,重排序在多線程環(huán)境下出現(xiàn)的概率還是挺高的,在關(guān)鍵字上有volatile和synchronized可以禁用重排序。wuu28資訊網(wǎng)——每日最新資訊28at.com

可能上面說的比較繞,舉個(gè)簡單的例子:wuu28資訊網(wǎng)——每日最新資訊28at.com

// x、y為非volatile變量// flag為volatile變量 x = 2; //語句1y = 0; //語句2flag = true; //語句3x = 4; //語句4y = -1; //語句5

10Java中的鎖:鎖的種類

在面試過程時(shí),經(jīng)常會(huì)被問到各種各樣的鎖,如樂觀鎖、讀寫鎖等等,非常繁多,在此做一個(gè)總結(jié)。介紹的內(nèi)容如下:wuu28資訊網(wǎng)——每日最新資訊28at.com

? 樂觀鎖/悲觀鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

? 獨(dú)享鎖/共享鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

? 互斥鎖/讀寫鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

? 可重入鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

? 公平鎖/非公平鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

? 分段鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

? 偏向鎖/輕量級(jí)鎖/重量級(jí)鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

? 自旋鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

樂觀鎖/悲觀鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

樂觀鎖與悲觀鎖并不是特指某兩種類型的鎖,是人們定義出來的概念或思想,主要是指看待并發(fā)同步的角度。wuu28資訊網(wǎng)——每日最新資訊28at.com

樂觀鎖:顧名思義,就是很樂觀,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人不會(huì)修改,所以不會(huì)上鎖,但是在更新的時(shí)候會(huì)判斷一下在此期間別人有沒有去更新這個(gè)數(shù)據(jù),可以使用版本號(hào)等機(jī)制。樂觀鎖適用于多讀的應(yīng)用類型,這樣可以提高吞吐量,在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實(shí)現(xiàn)方式CAS(Compare and Swap 比較并交換)實(shí)現(xiàn)的。wuu28資訊網(wǎng)——每日最新資訊28at.com

悲觀鎖:總是假設(shè)最壞的情況,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人會(huì)修改,所以每次在拿數(shù)據(jù)的時(shí)候都會(huì)上鎖,這樣別人想拿這個(gè)數(shù)據(jù)就會(huì)阻塞直到它拿到鎖。比如Java里面的synchronized關(guān)鍵字的實(shí)現(xiàn)就是悲觀鎖。wuu28資訊網(wǎng)——每日最新資訊28at.com

悲觀鎖適合寫操作非常多的場景,樂觀鎖適合讀操作非常多的場景,不加鎖會(huì)帶來大量的性能提升。wuu28資訊網(wǎng)——每日最新資訊28at.com

悲觀鎖在Java中的使用,就是利用各種鎖。wuu28資訊網(wǎng)——每日最新資訊28at.com

樂觀鎖在Java中的使用,是無鎖編程,常常采用的是CAS算法,典型的例子就是原子類,通過CAS自旋實(shí)現(xiàn)原子操作的更新。wuu28資訊網(wǎng)——每日最新資訊28at.com

獨(dú)享鎖/共享鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

 獨(dú)享鎖是指該鎖一次只能被一個(gè)線程所持有。wuu28資訊網(wǎng)——每日最新資訊28at.com

 共享鎖是指該鎖可被多個(gè)線程所持有。wuu28資訊網(wǎng)——每日最新資訊28at.com

 對(duì)于Java ReentrantLock而言,其是獨(dú)享鎖。但是對(duì)于Lock的另一個(gè)實(shí)現(xiàn)類ReadWriteLock,其讀鎖是共享鎖,其寫鎖是獨(dú)享鎖。wuu28資訊網(wǎng)——每日最新資訊28at.com

 讀鎖的共享鎖可保證并發(fā)讀是非常高效的,寫的過程是互斥的。wuu28資訊網(wǎng)——每日最新資訊28at.com

 對(duì)于synchronized而言,當(dāng)然是獨(dú)享鎖。wuu28資訊網(wǎng)——每日最新資訊28at.com

互斥鎖/讀寫鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

 獨(dú)享鎖/共享鎖就是一種廣義的說法,互斥鎖/讀寫鎖就是具體的實(shí)現(xiàn)。wuu28資訊網(wǎng)——每日最新資訊28at.com

 互斥鎖在Java中的具體實(shí)現(xiàn)就是ReentrantLock。wuu28資訊網(wǎng)——每日最新資訊28at.com

 讀寫鎖在Java中的具體實(shí)現(xiàn)就是ReadWriteLock。wuu28資訊網(wǎng)——每日最新資訊28at.com

可重入鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

 可重入鎖又名遞歸鎖,是指在同一個(gè)線程在外層方法獲取鎖的時(shí)候,在進(jìn)入內(nèi)層方法會(huì)自動(dòng)獲取鎖。說的有點(diǎn)抽象,下面會(huì)有一個(gè)代碼的示例。wuu28資訊網(wǎng)——每日最新資訊28at.com

對(duì)于Java ReetrantLock而言,從名字就可以看出是一個(gè)重入鎖,其名字是Reentrant Lock 重新進(jìn)入鎖。wuu28資訊網(wǎng)——每日最新資訊28at.com

對(duì)于synchronized而言,也是一個(gè)可重入鎖。可重入鎖的一個(gè)好處是可一定程度避免死鎖。wuu28資訊網(wǎng)——每日最新資訊28at.com

synchronized void setA() throws Exception{    Thread.sleep(1000);    setB();}synchronized void setB() throws Exception{    Thread.sleep(1000);}

上面的代碼就是一個(gè)可重入鎖的一個(gè)特點(diǎn)。如果不是可重入鎖的話,setB可能不會(huì)被當(dāng)前線程執(zhí)行,可能造成死鎖。wuu28資訊網(wǎng)——每日最新資訊28at.com

公平鎖/非公平鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

公平鎖是指多個(gè)線程按照申請(qǐng)鎖的順序來獲取鎖。wuu28資訊網(wǎng)——每日最新資訊28at.com

非公平鎖是指多個(gè)線程獲取鎖的順序并不是按照申請(qǐng)鎖的順序,有可能后申請(qǐng)的線程比先申請(qǐng)的線程優(yōu)先獲取鎖。wuu28資訊網(wǎng)——每日最新資訊28at.com

對(duì)于Java ReetrantLock而言,通過構(gòu)造函數(shù)指定該鎖是否是公平鎖,默認(rèn)是非公平鎖。非公平鎖的優(yōu)點(diǎn)在于吞吐量比公平鎖大。wuu28資訊網(wǎng)——每日最新資訊28at.com

對(duì)于synchronized而言,也是一種非公平鎖。wuu28資訊網(wǎng)——每日最新資訊28at.com

分段鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

分段鎖其實(shí)是一種鎖的設(shè)計(jì),并不是具體的一種鎖,對(duì)于ConcurrentHashMap而言,其并發(fā)的實(shí)現(xiàn)就是通過分段鎖的形式來實(shí)現(xiàn)高效的并發(fā)操作。wuu28資訊網(wǎng)——每日最新資訊28at.com

我們以ConcurrentHashMap來說一下分段鎖的含義以及設(shè)計(jì)思想,ConcurrentHashMap中的分段鎖稱為Segment,它即類似于HashMap(JDK7和JDK8中HashMap的實(shí)現(xiàn))的結(jié)構(gòu),即內(nèi)部擁有一個(gè)Entry數(shù)組,數(shù)組中的每個(gè)元素又是一個(gè)鏈表;同時(shí)又是一個(gè)ReentrantLock(Segment繼承了ReentrantLock)。wuu28資訊網(wǎng)——每日最新資訊28at.com

分段鎖的設(shè)計(jì)目的是細(xì)化鎖的粒度,當(dāng)操作不需要更新整個(gè)數(shù)組的時(shí)候,就僅僅針對(duì)數(shù)組中的一項(xiàng)進(jìn)行加鎖操作。wuu28資訊網(wǎng)——每日最新資訊28at.com

偏向鎖/輕量級(jí)鎖/重量級(jí)鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

偏向鎖是指一段同步代碼一直被一個(gè)線程所訪問,那么該線程會(huì)自動(dòng)獲取鎖。降低獲取鎖的代價(jià)。wuu28資訊網(wǎng)——每日最新資訊28at.com

輕量級(jí)鎖是指當(dāng)鎖是偏向鎖的時(shí)候,被另一個(gè)線程所訪問,偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖,其他線程會(huì)通過自旋的形式嘗試獲取鎖,不會(huì)阻塞,提高性能。wuu28資訊網(wǎng)——每日最新資訊28at.com

重量級(jí)鎖是指當(dāng)鎖為輕量級(jí)鎖的時(shí)候,另一個(gè)線程雖然是自旋,但自旋不會(huì)一直持續(xù)下去,當(dāng)自旋一定次數(shù)的時(shí)候,還沒有獲取到鎖,就會(huì)進(jìn)入阻塞,該鎖膨脹為重量級(jí)鎖。重量級(jí)鎖會(huì)讓他申請(qǐng)的線程進(jìn)入阻塞,性能降低。wuu28資訊網(wǎng)——每日最新資訊28at.com

自旋鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

 在Java中,自旋鎖是指嘗試獲取鎖的線程不會(huì)立即阻塞,而是采用循環(huán)的方式去嘗試獲取鎖,這樣的好處是減少線程上下文切換的消耗,缺點(diǎn)是循環(huán)會(huì)消耗CPU。wuu28資訊網(wǎng)——每日最新資訊28at.com

11、Java中的鎖:synchronized

synchronized是并發(fā)編程中接觸的最基本的同步關(guān)鍵字,是一種重量級(jí)鎖,也是java內(nèi)置的同步機(jī)制,首先我們知道synchronized提供了互斥性和可見性,那么我們可以通過使用它來保證并發(fā)的安全。wuu28資訊網(wǎng)——每日最新資訊28at.com

synchronized三種用法:wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 對(duì)象鎖

當(dāng)使用synchronized修飾類普通方法時(shí),那么當(dāng)前加鎖的級(jí)別就是實(shí)例對(duì)象,當(dāng)多個(gè)線程并發(fā)訪問該對(duì)象的同步方法、同步代碼塊時(shí),會(huì)進(jìn)行同步。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 類鎖

當(dāng)使用synchronized修飾類靜態(tài)方法時(shí),那么當(dāng)前加鎖的級(jí)別就是類,當(dāng)多個(gè)線程并發(fā)訪問該類(所有實(shí)例對(duì)象)的同步方法以及同步代碼塊時(shí),會(huì)進(jìn)行同步。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 同步代碼塊

當(dāng)使用synchronized修飾代碼塊時(shí),那么當(dāng)前加鎖的級(jí)別就是synchronized(X)中配置的x對(duì)象實(shí)例,當(dāng)多個(gè)線程并發(fā)訪問該對(duì)象的同步方法、同步代碼塊以及當(dāng)前的代碼塊時(shí),會(huì)進(jìn)行同步。wuu28資訊網(wǎng)——每日最新資訊28at.com

使用同步代碼塊時(shí)要注意的是不要使用String類型對(duì)象,因?yàn)?strong>String常量池的存在,所以很容易導(dǎo)致出問題。wuu28資訊網(wǎng)——每日最新資訊28at.com

synchronized 同步代碼塊

synchronized 同步代碼塊解決線程安全問題wuu28資訊網(wǎng)——每日最新資訊28at.com

什么是線程安全問題: 多線程操作共享數(shù)據(jù),導(dǎo)致共享數(shù)據(jù)出現(xiàn)錯(cuò)亂wuu28資訊網(wǎng)——每日最新資訊28at.com

出現(xiàn)線程安全問題的條件:
1.有多個(gè)線程
2.有共享數(shù)據(jù)
3.多線程操作共享數(shù)據(jù)wuu28資訊網(wǎng)——每日最新資訊28at.com

代碼:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;/** 學(xué)習(xí)使用同步代碼塊解決線程安全問題 */public class Test8 { // 定義票的總數(shù)量 private static int ticket = 100; public static void main(String[] args) { Runnable runnable = () ->{ // 循環(huán)買票 while (true){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // 同步代碼塊(類鎖) synchronized (Test8.class) { if (ticket > 0) { ticket--; System.out.println(Thread.currentThread().getName() + "賣了一張票,剩余:" + ticket); } else { // 票沒了 break; } } } }; // 創(chuàng)建3個(gè)線程 Thread t1 = new Thread(runnable, "窗口1"); Thread t2 = new Thread(runnable, "窗口2"); Thread t3 = new Thread(runnable, "窗口3"); t1.start(); t2.start(); t3.start(); }}

小結(jié)wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 哪些對(duì)象可以作為鎖?

任意對(duì)象。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • synchronized鎖對(duì)象時(shí)候要注意什么?

多線程并發(fā)方法同步代碼塊,需要鎖同一個(gè)對(duì)象。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • synchronized中的鎖的作用是什么?

同步代碼塊中有鎖的線程進(jìn)入,無鎖的線程需要等待。wuu28資訊網(wǎng)——每日最新資訊28at.com

synchronized 同步方法wuu28資訊網(wǎng)——每日最新資訊28at.com

synchronized 同步方法解決線程安全問題wuu28資訊網(wǎng)——每日最新資訊28at.com

語法wuu28資訊網(wǎng)——每日最新資訊28at.com

// 普通同步方法,對(duì)當(dāng)前一個(gè)實(shí)例對(duì)象加鎖,多線程操作同一個(gè)對(duì)象實(shí)例進(jìn)行同步操作public synchronized void 方法名() { ...}// 靜態(tài)同步方法,對(duì)類加鎖,多線程操作當(dāng)前類所有實(shí)例對(duì)象進(jìn)行同步操作public static synchronized void 方法名() { ...}

代碼:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;/** 學(xué)習(xí)使用同步代碼塊解決線程安全問題 */public class Test9 { // 定義票的總數(shù)量 private static int ticket = 100; public static void main(String[] args) { Runnable runnable = () ->{ // 循環(huán)買票 while (true){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } sell(); } }; // 創(chuàng)建3個(gè)線程 Thread t1 = new Thread(runnable, "窗口1"); Thread t2 = new Thread(runnable, "窗口2"); Thread t3 = new Thread(runnable, "窗口3"); t1.start(); t2.start(); t3.start(); } // 同步方法 private static synchronized void sell() { // 同步代碼塊(類鎖) if (ticket > 0) { ticket--; System.out.println(Thread.currentThread().getName() + "賣了一張票,剩余:" + ticket); } }}

小結(jié)wuu28資訊網(wǎng)——每日最新資訊28at.com

synchronized是通過對(duì)象內(nèi)部的一個(gè)叫==監(jiān)視器鎖==來實(shí)現(xiàn)的,但是監(jiān)視器鎖本質(zhì)又是依賴底層的操作系統(tǒng)的Mutex(互斥) Lock來實(shí)現(xiàn)的,而操系統(tǒng)實(shí)現(xiàn)線程之間的切換會(huì)造成帶量CPU資源浪費(fèi),這個(gè)成本非常的高,狀態(tài)之間的轉(zhuǎn)換需要相對(duì)比較長的時(shí)間,這就是為什么Synchronized效率低的原因,因此這種依賴于操作系統(tǒng)Mutex Lock所實(shí)現(xiàn)的鎖我們稱之為:==重量級(jí)鎖==。JDK中對(duì)Synchronized做的種種優(yōu)化,其核心都是為了減少這種重量級(jí)鎖的使用,JDK1.5以后,為來減少獲得鎖和釋放鎖所帶來的性能消耗, JDK引入了:”輕量級(jí)鎖“和”偏向鎖“進(jìn)行優(yōu)化,這個(gè)優(yōu)化自動(dòng)的無需開發(fā)人員介入。wuu28資訊網(wǎng)——每日最新資訊28at.com

synchronized 屬于最基本的線程通信機(jī)制,基于對(duì)象監(jiān)視器實(shí)現(xiàn)的。Java中的每個(gè)對(duì)象都與一個(gè)監(jiān)視器相關(guān)聯(lián),一個(gè)線程可以鎖定或解鎖。一次只有一個(gè)線程可以鎖定監(jiān)視器。試圖鎖定該監(jiān)視器的任何其他線程都會(huì)被阻塞,直到它們可以獲得該監(jiān)視器上的鎖定為止。wuu28資訊網(wǎng)——每日最新資訊28at.com

12、Java中的鎖:ReentrantLock

目標(biāo)學(xué)習(xí)使用ReentrantLock可重入鎖解決線程安全問題wuu28資訊網(wǎng)——每日最新資訊28at.com

介紹wuu28資訊網(wǎng)——每日最新資訊28at.com

  • synchronized 是內(nèi)部鎖,自動(dòng)化的上鎖與釋放鎖,而lock是手動(dòng)的,需要人為的上鎖和釋放鎖,lock比較靈活,但是代碼相對(duì)較多
  • lock接口異常的時(shí)候不會(huì)自動(dòng)的釋放鎖,同樣需要手動(dòng)的釋放鎖,所以一般寫在finally語句塊中,而synchronized則會(huì)在異常的時(shí)候自動(dòng)的釋放鎖

API方法wuu28資訊網(wǎng)——每日最新資訊28at.com

  • lock()

用來獲取鎖,如果鎖被其他線程獲取,處于等待狀態(tài)。如果采用Lock,必須主動(dòng)去釋放鎖,并且在發(fā)生異常的時(shí)候,不會(huì)自動(dòng)釋放鎖。因此一般來說,使用Lock必須早try{}catch{}塊中進(jìn)行,并且將釋放鎖的操作放在finally塊中進(jìn)行,以保證鎖一定被釋放,防止死鎖發(fā)生。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • lockInterruptibly()

通過這個(gè)這個(gè)方法去獲取鎖時(shí),如果線程正在等待獲取鎖,則這個(gè)線程能夠響應(yīng)中斷,即中斷線程的等待狀態(tài)。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • tryLock()

tryLock方法是有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已經(jīng)由其他線程獲取),則返回false,也就是說這個(gè)方法無論如何都會(huì)立即返回。在獲取不到鎖的時(shí)候,不會(huì)再那一直等待。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • tryLock(long time, TimeUnit unit)

與tryLock類似,只不過是有等待時(shí)間,在等待時(shí)間內(nèi)獲取到鎖返回true,超時(shí)返回false。wuu28資訊網(wǎng)——每日最新資訊28at.com

  • unlock()

釋放鎖,一定要在finally塊中釋放。wuu28資訊網(wǎng)——每日最新資訊28at.com

Lock鎖使用語法wuu28資訊網(wǎng)——每日最新資訊28at.com

Lock介紹:  wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 比synchronized更靈活,可以自己調(diào)用方法    
  • void lock() 獲得鎖    
  • void unlock() 釋放鎖

Lock實(shí)現(xiàn)類:  wuu28資訊網(wǎng)——每日最新資訊28at.com

ReentrantLockwuu28資訊網(wǎng)——每日最新資訊28at.com

Lock使用標(biāo)準(zhǔn)方式wuu28資訊網(wǎng)——每日最新資訊28at.com

l.lock(); // 獲得鎖    try {        操作共享資源的代碼    } finally {        l.unlock(); // 釋放鎖    }

代碼:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;import java.util.concurrent.locks.ReentrantLock;/** 學(xué)習(xí)使用Lock解決線程安全問題 */public class Test10 {    // 定義票的總數(shù)量    private static int ticket = 100;    public static void main(String[] args) {        // 創(chuàng)建可重入鎖對(duì)象        ReentrantLock lock = new ReentrantLock();        Runnable runnable = () -> {            // 循環(huán)賣票            while (true) {                try {                    Thread.sleep(10);                    // 獲得鎖                    lock.lock();                    if (ticket > 0) {                        ticket--;                        System.out.println(Thread.currentThread().getName() +                                "賣了一張票,剩余:" + ticket);                    } else {                        break;                    }                } catch (InterruptedException e) {                    e.printStackTrace();                } finally {                    // 釋放鎖                    lock.unlock();                }            }        };        // 創(chuàng)建3個(gè)線程        Thread t1 = new Thread(runnable, "窗口1");        Thread t2 = new Thread(runnable, "窗口2");        Thread t3 = new Thread(runnable, "窗口3");        t1.start();        t2.start();        t3.start();    }}

13、Java中的鎖:ReentrantReadWriteLock

目標(biāo):掌握Readwriterlock讀寫鎖分析和場景wuu28資訊網(wǎng)——每日最新資訊28at.com

ReadWriteLock接口介紹wuu28資訊網(wǎng)——每日最新資訊28at.com

ReadWriteLock也是一個(gè)接口,在它里面只定義了兩個(gè)方法:wuu28資訊網(wǎng)——每日最新資訊28at.com

package java.util.concurrent.locks;public interface ReadWriteLock { Lock readLock(); Lock writeLock();}

一個(gè)用來獲取讀鎖,一個(gè)用來獲取寫鎖。也就是說將文件的讀寫操作分開,分成2個(gè)鎖來分配給線程,從而使得多個(gè)線程可以同時(shí)進(jìn)行讀操作。下面的ReentrantReadWriteLock實(shí)現(xiàn)了ReadWriteLock接口。 wuu28資訊網(wǎng)——每日最新資訊28at.com

ReentrantReadWriteLock介紹wuu28資訊網(wǎng)——每日最新資訊28at.com

ReentrantReadWriteLock里面提供了很多豐富的方法,不過最主要的有兩個(gè)方法readLock() 和 writeLock()用來獲取讀鎖和寫鎖。 wuu28資訊網(wǎng)——每日最新資訊28at.com

ReentrantReadWriteLock可重入讀寫鎖wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;import java.util.concurrent.locks.ReentrantReadWriteLock;// ReentrantReadWriteLock: 可重入讀寫鎖 (讀鎖是共享鎖,寫鎖是獨(dú)享鎖)public class Test11 { // 定義可重入讀寫鎖 private ReentrantReadWriteLock rw = new ReentrantReadWriteLock(); public static void main(String[] args) { final Test11 test = new Test11();  new Thread(() -> { test.get(Thread.currentThread()); }).start(); new Thread(() -> { test.get(Thread.currentThread()); }).start(); } public void get(Thread thread) { // 讀鎖是共享鎖 rw.readLock().lock(); // 寫鎖是獨(dú)享鎖 //rw.writeLock().lock(); try { for (int i = 0; i < 50; i++){ System.out.println(thread.getName() + "正在進(jìn)行讀操作"); } System.out.println(thread.getName() + "讀操作完畢"); }finally { rw.readLock().unlock(); //rw.writeLock().unlock(); } }}

小結(jié)wuu28資訊網(wǎng)——每日最新資訊28at.com

ReentrantReadWriteLock的優(yōu)勢與應(yīng)用場景wuu28資訊網(wǎng)——每日最新資訊28at.com

1. 大大提升了讀操作的效率。

2. 不過要注意的是,如果有一個(gè)線程已經(jīng)占用了讀鎖,則此時(shí)其他線程如果要申請(qǐng)寫鎖,則申請(qǐng)寫鎖的線程會(huì)一直等待釋放讀鎖。

3. 如果有一個(gè)線程已經(jīng)占用了寫鎖,則此時(shí)其他線程如果申請(qǐng)寫鎖或者讀鎖,則申請(qǐng)的線程會(huì)一直等待釋放寫鎖。

4. ReentrantReadWriteLock適合讀多寫少的應(yīng)用場景wuu28資訊網(wǎng)——每日最新資訊28at.com

14原子更新基本類型

目標(biāo)掌握java.util.concurrent.atomic包下原子更新基本類型wuu28資訊網(wǎng)——每日最新資訊28at.com

介紹wuu28資訊網(wǎng)——每日最新資訊28at.com

Java從JDK 1.5開始提供了java.util.concurrent.atomic包(以下簡稱Atomic包),這個(gè)包中的原子操作類提供了一種用法簡單、性能高效、線程安全地更新一個(gè)變量的方式。actomic實(shí)現(xiàn)原子性操作采用的是CAS算法保證原子性操作,性能高效。wuu28資訊網(wǎng)——每日最新資訊28at.com

CAS原理分析:wuu28資訊網(wǎng)——每日最新資訊28at.com

使用CAS(Compare-And-Swap)比較并交換,操作保證數(shù)據(jù)原子性wuu28資訊網(wǎng)——每日最新資訊28at.com

CAS算法是 JDK對(duì)并發(fā)操作共享數(shù)據(jù)的支持,包含了3個(gè)操作數(shù) wuu28資訊網(wǎng)——每日最新資訊28at.com

第一個(gè)操作數(shù)內(nèi)存值value(V) wuu28資訊網(wǎng)——每日最新資訊28at.com

第二個(gè)操作數(shù)預(yù)估值expect(E) wuu28資訊網(wǎng)——每日最新資訊28at.com

第三個(gè)操作數(shù)更新值new(N)wuu28資訊網(wǎng)——每日最新資訊28at.com

含義當(dāng)多線程每個(gè)線程執(zhí)行寫的操作時(shí),每個(gè)線程都會(huì)讀取主存最新內(nèi)存值value,并設(shè)置預(yù)估的值,只有最新內(nèi)存值與預(yù)估值一致的線程,就會(huì)將需要更新的值更新到主存中,其他線程就會(huì)失敗保證原子性操作;這樣就解決了synchronized排隊(duì)導(dǎo)致性能低下的問題。wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

java.util.concurrent.atomic包的原子類:wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

原子更新基本類型wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

含義wuu28資訊網(wǎng)——每日最新資訊28at.com

AtomicBooleanwuu28資訊網(wǎng)——每日最新資訊28at.com

原子更新布爾類型wuu28資訊網(wǎng)——每日最新資訊28at.com

AtomicIntegerwuu28資訊網(wǎng)——每日最新資訊28at.com

原子更新整型wuu28資訊網(wǎng)——每日最新資訊28at.com

AtomicLongwuu28資訊網(wǎng)——每日最新資訊28at.com

原子更新長整型wuu28資訊網(wǎng)——每日最新資訊28at.com

上面3個(gè)類提供方法完全一樣,所以我們以AtomicInteger為例進(jìn)行講解API方法:wuu28資訊網(wǎng)——每日最新資訊28at.com

方法wuu28資訊網(wǎng)——每日最新資訊28at.com

含義wuu28資訊網(wǎng)——每日最新資訊28at.com

int addAndGet(int delta)wuu28資訊網(wǎng)——每日最新資訊28at.com

以原子方式將輸入的數(shù)值與實(shí)例中的值(AtomicInteger里的value)相加,并返回結(jié)果wuu28資訊網(wǎng)——每日最新資訊28at.com

boolean compareAndSet(int expect,int update)wuu28資訊網(wǎng)——每日最新資訊28at.com

如果輸入的數(shù)值等于預(yù)期值,則以原子方式將該值設(shè)置為輸入的值wuu28資訊網(wǎng)——每日最新資訊28at.com

int getAndIncrement()wuu28資訊網(wǎng)——每日最新資訊28at.com

以原子方式將當(dāng)前值加1,注意,這里返回的是自增前的值wuu28資訊網(wǎng)——每日最新資訊28at.com

void lazySet(int newValue)wuu28資訊網(wǎng)——每日最新資訊28at.com

最終會(huì)設(shè)置成newValue,使用lazySet設(shè)置值后,可能導(dǎo)致其他線程在之后的一小段時(shí)間內(nèi)還是可以讀到舊的值wuu28資訊網(wǎng)——每日最新資訊28at.com

int getAndSet(int newValue)wuu28資訊網(wǎng)——每日最新資訊28at.com

以原子方式設(shè)置為newValue的值,并返回舊值。wuu28資訊網(wǎng)——每日最新資訊28at.com

int incrementAndGet()wuu28資訊網(wǎng)——每日最新資訊28at.com

以原子方式將當(dāng)前值加1,注意,這里返回的是自增后的值wuu28資訊網(wǎng)——每日最新資訊28at.com

代碼:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;import java.util.ArrayList;import java.util.List;import java.util.concurrent.atomic.AtomicInteger;public class Test12 implements Runnable { // 定義整型并發(fā)原子對(duì)象 private static AtomicInteger atomicInteger = new AtomicInteger(0); @Override public void run() { try { // 線程休眠 Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } // 進(jìn)行原子性操作+1 System.out.println(atomicInteger.incrementAndGet()); } public static void main(String[] args) throws InterruptedException { // 創(chuàng)建List集合 List<Thread> list = new ArrayList<>(); Test12 task = new Test12(); // 開啟多線程進(jìn)行操作共享變量 for (int i = 0; i <10 ; i++) { Thread thread = new Thread(task); list.add(thread); thread.start(); } for (Thread thread : list) { thread.join(); // 確保所有thread全部運(yùn)行 } System.out.println("遞增結(jié)果:" + atomicInteger.get()); }}

運(yùn)行效果wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com


wuu28資訊網(wǎng)——每日最新資訊28at.com

15原子更新數(shù)組類型

目標(biāo)掌握java.util.concurrent.atomic包下原子更新數(shù)組類型wuu28資訊網(wǎng)——每日最新資訊28at.com

介紹wuu28資訊網(wǎng)——每日最新資訊28at.com

通過原子的方式更新數(shù)組里的某個(gè)元素,Atomic包提供了以下3個(gè)類。這幾個(gè)類提供的方法幾乎一樣,所以本節(jié)僅以AtomicIntegerArray為例進(jìn)行講解。wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

含義wuu28資訊網(wǎng)——每日最新資訊28at.com

AtomicIntegerArraywuu28資訊網(wǎng)——每日最新資訊28at.com

原子更新整型數(shù)組里的元素wuu28資訊網(wǎng)——每日最新資訊28at.com

AtomicLongArraywuu28資訊網(wǎng)——每日最新資訊28at.com

原子更新長整型數(shù)組里的元素wuu28資訊網(wǎng)——每日最新資訊28at.com

AtomicReferenceArraywuu28資訊網(wǎng)——每日最新資訊28at.com

原子更新引用類型數(shù)組里的元素wuu28資訊網(wǎng)——每日最新資訊28at.com

AtomicIntegerArray類:wuu28資訊網(wǎng)——每日最新資訊28at.com

方法wuu28資訊網(wǎng)——每日最新資訊28at.com

含義wuu28資訊網(wǎng)——每日最新資訊28at.com

int addAndGet(int i,int delta)wuu28資訊網(wǎng)——每日最新資訊28at.com

以原子方式將輸入值與數(shù)組中索引i的元素相加wuu28資訊網(wǎng)——每日最新資訊28at.com

boolean compareAndSet(int i,int expect,int update)wuu28資訊網(wǎng)——每日最新資訊28at.com

如果當(dāng)前值等于預(yù)期值,則以原子方式將數(shù)組位置i的元素設(shè)置成update值wuu28資訊網(wǎng)——每日最新資訊28at.com

代碼:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;import java.util.ArrayList;import java.util.List;import java.util.concurrent.atomic.AtomicIntegerArray;public class Test13 implements Runnable{ // 定義數(shù)組 private static int[] ints = {0,2,3}; // 定義原子整型數(shù)組對(duì)象 private static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(ints); @Override public void run() { try { // 線程休眠 Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } // 操作普通數(shù)組中的第一個(gè)元素 +1 ints[0] = ints[0] + 1; // 操作原子數(shù)組中的第一個(gè)元素 +1 System.out.println(atomicIntegerArray.incrementAndGet(0)); } public static void main(String[] args) throws InterruptedException { // 創(chuàng)建List集合 List<Thread> list = new ArrayList<>(); Test13 task = new Test13(); // 開啟多線程進(jìn)行操作共享變量 for (int i = 0; i <10 ; i++) { Thread thread = new Thread(task); list.add(thread); thread.start(); } for (Thread thread : list) { thread.join(); // 確保所有thread全部運(yùn)行 } System.out.println("原子數(shù)組操作結(jié)果:" + atomicIntegerArray.get(0)); // 10 System.out.println("普通數(shù)組操作結(jié)果:" + ints[0]); // 不一定 }}

運(yùn)行效果wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

注意:wuu28資訊網(wǎng)——每日最新資訊28at.com

數(shù)組value通過構(gòu)造方法傳遞進(jìn)去,然后AtomicIntegerArray會(huì)將當(dāng)前數(shù)組復(fù)制一份,所以當(dāng)AtomicIntegerArray對(duì)內(nèi)部的數(shù)組元素進(jìn)行修改時(shí),不會(huì)影響傳入的數(shù)組。wuu28資訊網(wǎng)——每日最新資訊28at.com

16原子更新引用類型

目標(biāo)使用原子操作更新引用類型數(shù)據(jù)(也就是原子更新多個(gè)變量)wuu28資訊網(wǎng)——每日最新資訊28at.com

介紹wuu28資訊網(wǎng)——每日最新資訊28at.com

原子更新基本類型的AtomicInteger,只能更新一個(gè)變量,如果要原子更新多個(gè)變量,就需要使用這個(gè)原子更新引用類型提供的類。Atomic包提供了以下3個(gè)類:wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

介紹wuu28資訊網(wǎng)——每日最新資訊28at.com

AtomicReferencewuu28資訊網(wǎng)——每日最新資訊28at.com

原子更新引用類型wuu28資訊網(wǎng)——每日最新資訊28at.com

AtomicReferenceFieldUpdaterwuu28資訊網(wǎng)——每日最新資訊28at.com

原子更新引用類型里的字段wuu28資訊網(wǎng)——每日最新資訊28at.com

AtomicMarkableReferencewuu28資訊網(wǎng)——每日最新資訊28at.com

原子更新帶有標(biāo)記位的引用類型??梢栽痈乱粋€(gè)布爾類型的標(biāo)記位和引用類型。構(gòu)造方法AtomicMarkableReference(V initialRef,boolean initialMark)wuu28資訊網(wǎng)——每日最新資訊28at.com

AtomicReference類:wuu28資訊網(wǎng)——每日最新資訊28at.com

方法wuu28資訊網(wǎng)——每日最新資訊28at.com

介紹wuu28資訊網(wǎng)——每日最新資訊28at.com

boolean compareAndSet(V expect, V update)wuu28資訊網(wǎng)——每日最新資訊28at.com

如果是期望值expect與當(dāng)前內(nèi)存值一樣,更新為updatewuu28資訊網(wǎng)——每日最新資訊28at.com

代碼:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;import java.util.concurrent.atomic.AtomicReference;public class Test14 { public static void main(String[] args) { // 1. 創(chuàng)建一個(gè)User對(duì)象封裝數(shù)據(jù) User user = new User("李小華", 18); // 2. 創(chuàng)建一個(gè)原子引用類型AtomicReference操作User類型數(shù)據(jù) AtomicReference<User> atomicReference = new AtomicReference<>(); // 3. 將user對(duì)象的數(shù)據(jù)存入原子引用類型對(duì)象中 atomicReference.set(user); // 4. 更新原子引用類型存儲(chǔ)的數(shù)據(jù) atomicReference.compareAndSet(user, new User("李中華", 20)); // 5. 打印普通user對(duì)象數(shù)據(jù)與原子引用類型對(duì)象數(shù)據(jù) System.out.println("普通對(duì)象數(shù)據(jù):"+ user +",對(duì)象hashcode: " + user.hashCode()); System.out.println("原子引用類型對(duì)象數(shù)據(jù):" + atomicReference.get() + ",對(duì)象hashcode: " + atomicReference.get().hashCode()); }}

運(yùn)行效果wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

17、并發(fā)工具類:CountDownLatch(倒計(jì)數(shù)閉鎖)

目標(biāo):掌握CountDownLatch使用(實(shí)現(xiàn)等待其他線程處理完才繼續(xù)運(yùn)行當(dāng)前線程)wuu28資訊網(wǎng)——每日最新資訊28at.com

介紹wuu28資訊網(wǎng)——每日最新資訊28at.com

CountDownLatch是一個(gè)同步輔助類,也叫倒計(jì)數(shù)閉鎖,在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個(gè)或多個(gè)線程一直等待。用給定的計(jì)數(shù)初始化 CountDownLatch。這個(gè)輔助類可以進(jìn)行計(jì)算遞減,所以在當(dāng)前計(jì)數(shù)到達(dá)零之前,可以讓現(xiàn)場一直受阻塞。到達(dá)0之后,會(huì)釋放所有等待的線程,執(zhí)行后續(xù)操作。wuu28資訊網(wǎng)——每日最新資訊28at.com

CountDownLatch類wuu28資訊網(wǎng)——每日最新資訊28at.com

說明wuu28資訊網(wǎng)——每日最新資訊28at.com

CountDownLatch(int count)wuu28資訊網(wǎng)——每日最新資訊28at.com

創(chuàng)建CountDownLatch 實(shí)例并設(shè)置預(yù)定計(jì)數(shù)次數(shù)。wuu28資訊網(wǎng)——每日最新資訊28at.com

void countDown()wuu28資訊網(wǎng)——每日最新資訊28at.com

遞減鎖存器的計(jì)數(shù),如果計(jì)數(shù)到達(dá)零,則釋放所有等待的線程。如果當(dāng)前計(jì)數(shù)大于零,則將計(jì)數(shù)減少1。wuu28資訊網(wǎng)——每日最新資訊28at.com

void await()wuu28資訊網(wǎng)——每日最新資訊28at.com

使當(dāng)前線程在鎖存器倒計(jì)數(shù)至零之前一直等待,除非線程被中斷。如果當(dāng)前的計(jì)數(shù)為零,則此方法立即返回。wuu28資訊網(wǎng)——每日最新資訊28at.com

CountDownLatch 是通過一個(gè)計(jì)數(shù)器來實(shí)現(xiàn)的,計(jì)數(shù)器的初始值為線程的數(shù)量。每當(dāng)一個(gè)線程完成了自己的任務(wù)后,計(jì)數(shù)器的值就會(huì)減 1。當(dāng)計(jì)數(shù)器值到達(dá) 0 時(shí),表示所有的線程已經(jīng)完成了任務(wù),然后在閉鎖上等待的線程就可以恢復(fù)執(zhí)行任務(wù)。wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

傳統(tǒng)join阻塞案例代碼:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;public class Test15 { public static void main(String[] args) throws InterruptedException { // 創(chuàng)建線程1 Thread t1 = new Thread(() -> { System.out.println("parser1 finish"); }); // 創(chuàng)建線程2 Thread t2 = new Thread(() -> { System.out.println("parser2 finish"); }); t1.start(); t2.start(); t1.join(); // join阻塞 t2.join(); // join阻塞 System.out.println("join方式: all parser finish"); }}

join阻塞效果:wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

使用CountDownLatch實(shí)現(xiàn)阻塞代碼優(yōu)化:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;import java.util.concurrent.CountDownLatch;public class Test16 { // 定義倒計(jì)數(shù)閉鎖對(duì)象 private static CountDownLatch countDownLatch = new CountDownLatch(2); public static void main(String[] args) throws InterruptedException { // 創(chuàng)建線程1 Thread t1 = new Thread(() -> { System.out.println("parser1 finish"); countDownLatch.countDown(); // 計(jì)算遞減1 }); // 創(chuàng)建線程2 Thread t2 = new Thread(() -> { System.out.println("parser2 finish"); countDownLatch.countDown(); // 計(jì)算遞減1 }); t1.start(); t2.start(); countDownLatch.await(); // 阻塞,計(jì)算為0釋放阻塞,運(yùn)行后面的代碼 System.out.println("join方式: all parser finish"); }}

使用CountDownLatch實(shí)現(xiàn)阻塞效果:wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

CountDownLatch倒計(jì)數(shù)閉鎖好處實(shí)現(xiàn)線程最大并發(fā)執(zhí)行。wuu28資訊網(wǎng)——每日最新資訊28at.com

18、并發(fā)工具類:CyclicBarrier(同步屏障)

目標(biāo)掌握CyclicBarrier的使用wuu28資訊網(wǎng)——每日最新資訊28at.com

介紹wuu28資訊網(wǎng)——每日最新資訊28at.com

CyclicBarrier是JDK 1.5的 java.util.concurrent 并發(fā)包中提供的一個(gè)并發(fā)工具類。 wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 謂 Cyclic 即循環(huán)的意思,所謂 Barrier 即屏障的意思。
  • CyclicBarrier (可重用屏障/柵欄)類似于 CountDownLatch功能一樣,都有讓多個(gè)線程等待同步然后再開始下一步動(dòng)作。
  • CyclicBarrier 可以使一定數(shù)量的線程反復(fù)地在屏障位置處匯集。當(dāng)線程到達(dá)屏障位置時(shí)將調(diào)用 await() 方法,這個(gè)方法將阻塞直到所有線程都到達(dá)屏障位置。如果所有線程都到達(dá)屏障位置,那么屏障將打開,此時(shí)所有的線程都將被釋放,而屏障將被重置以便下次使用。

wuu28資訊網(wǎng)——每日最新資訊28at.com

CyclicBarrier類wuu28資訊網(wǎng)——每日最新資訊28at.com

說明wuu28資訊網(wǎng)——每日最新資訊28at.com

CyclicBarrier(int parties)wuu28資訊網(wǎng)——每日最新資訊28at.com

創(chuàng)建對(duì)象,參數(shù)表示屏障攔截的線程數(shù)量,初始化相互等待的線程數(shù)量wuu28資訊網(wǎng)——每日最新資訊28at.com

int await()wuu28資訊網(wǎng)——每日最新資訊28at.com

告訴CyclicBarrier自己已經(jīng)到達(dá)了屏障,然后當(dāng)前線程被阻塞返回值int為達(dá)到屏障器的索引: 索引未達(dá)到屏障線程數(shù)量-1,0表示最后一個(gè)達(dá)到屏障wuu28資訊網(wǎng)——每日最新資訊28at.com

int getParties()wuu28資訊網(wǎng)——每日最新資訊28at.com

獲取 CyclicBarrier 打開屏障的線程數(shù)量wuu28資訊網(wǎng)——每日最新資訊28at.com

void reset()wuu28資訊網(wǎng)——每日最新資訊28at.com

使CyclicBarrier回歸初始狀態(tài),它做了兩件事。如果有正在等待的線程,則會(huì)拋出 BrokenBarrierException 異常,且這些線程停止等待,繼續(xù)執(zhí)行。將是否破損標(biāo)志位broken置為 false。wuu28資訊網(wǎng)——每日最新資訊28at.com

boolean isBroken()wuu28資訊網(wǎng)——每日最新資訊28at.com

獲取是否破損標(biāo)志位broken的值,此值有以下幾種情況。1.CyclicBarrier初始化時(shí),broken=false表示屏障未破損。 2.如果正在等待的線程被中斷則broken=true,表示屏障破損。 3.如果正在等待的線程超時(shí), 則broken=true,表示屏障破損。 4.如果有線程調(diào)用 CyclicBarrier.reset() 方法,則broken=false,表示屏障回到未破損狀態(tài)。wuu28資訊網(wǎng)——每日最新資訊28at.com

int getNumberWaiting()wuu28資訊網(wǎng)——每日最新資訊28at.com

獲取達(dá)到屏障阻塞等待的線程數(shù)wuu28資訊網(wǎng)——每日最新資訊28at.com

CyclicBarrier(int parties,Runnable barrierAction)wuu28資訊網(wǎng)——每日最新資訊28at.com

用于所有線程到達(dá)屏障時(shí),優(yōu)先執(zhí)行barrierAction的線程wuu28資訊網(wǎng)——每日最新資訊28at.com

代碼:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;import java.util.concurrent.CyclicBarrier;import java.util.concurrent.TimeUnit;public class Test17 {    // 定義同步屏障對(duì)象    private static CyclicBarrier cb = new CyclicBarrier(2);    public static void main(String[] args) {        // 創(chuàng)建線程1        new Thread(() -> {            try {                System.out.println("達(dá)到屏障阻塞線程數(shù):" + cb.getNumberWaiting());                cb.await(); // 達(dá)到屏障阻塞,+1                System.out.println("運(yùn)行結(jié)束1"); // 不會(huì)運(yùn)行            } catch (Exception e) {                e.printStackTrace();            }        }).start();        // 創(chuàng)建線程2        new Thread(() -> {            try {                System.out.println("達(dá)到屏障阻塞線程數(shù):" + cb.getNumberWaiting());                cb.await(); // 達(dá)到屏障阻塞,+1                System.out.println("運(yùn)行結(jié)束2"); // 不會(huì)運(yùn)行            } catch (Exception e) {                e.printStackTrace();            }        }).start();        try {            TimeUnit.SECONDS.sleep(2);            // 會(huì)運(yùn)行,沒有到達(dá)屏障,不會(huì)阻塞            System.out.println("主線程完成,攔截線程數(shù):" + cb.getParties()                    + ",達(dá)到屏障阻塞線程數(shù):" + cb.getNumberWaiting());        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

運(yùn)行效果wuu28資訊網(wǎng)——每日最新資訊28at.com

由于兩個(gè)子線程的調(diào)度是由CPU決定的,兩個(gè)子線程都有可能先執(zhí)行,所以會(huì)產(chǎn)生兩種輸出:wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

說明:由于所有線程都達(dá)到屏障,所有阻塞線程被釋放,所以阻塞線程為0wuu28資訊網(wǎng)——每日最新資訊28at.com

如果把new CyclicBarrier(2)修改成new CyclicBarrier(3),則主線程和子線程會(huì)永遠(yuǎn)等待,因?yàn)闆]有第三個(gè)線程執(zhí)行await方法,即沒有第三個(gè)線程到達(dá)屏障,所以之前到達(dá)屏障的兩個(gè)線程都不會(huì)繼續(xù)執(zhí)行。wuu28資訊網(wǎng)——每日最新資訊28at.com

private static CyclicBarrier cb = new CyclicBarrier(3);

wuu28資訊網(wǎng)——每日最新資訊28at.com

19、并發(fā)工具類:Semaphore(信號(hào)量)

目標(biāo):掌握Semaphore的使用wuu28資訊網(wǎng)——每日最新資訊28at.com

介紹wuu28資訊網(wǎng)——每日最新資訊28at.com

Semaphore(信號(hào)量)限制著訪問某些資源的線程數(shù)量,在到達(dá)限制的線程數(shù)量之前,線程能夠繼續(xù)進(jìn)行資源的訪問,一旦訪問資源的數(shù)量到達(dá)限制的線程數(shù)量,這個(gè)時(shí)候線程就不能夠再去獲取資源,只有等待有線程退出資源的獲取。wuu28資訊網(wǎng)——每日最新資訊28at.com

應(yīng)用場景wuu28資訊網(wǎng)——每日最新資訊28at.com

比如模擬一個(gè)停車場停車信號(hào),假設(shè)停車場只有兩個(gè)車位,一開始兩個(gè)車位都是空的。這時(shí)同時(shí)來了兩輛車,看門人允許它們進(jìn)入停車場,然后放下車攔。以后來的車必須在入口等待,直到停車場中有車輛離開。這時(shí),如果有一輛車離開停車場,看門人得知后,打開車攔,放入一輛,如果又離開一輛,則又可以放入一輛,如此往復(fù)。wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

API方法wuu28資訊網(wǎng)——每日最新資訊28at.com

信號(hào)量維護(hù)了一個(gè)許可集。如有必要,在許可可用前會(huì)阻塞每一個(gè) acquire(),然后再獲取該許可。每個(gè) release() 添加一個(gè)許可,從而可能釋放一個(gè)正在阻塞的獲取者讓其運(yùn)行。但是,不使用實(shí)際的許可對(duì)象,Semaphore 只對(duì)可用許可的號(hào)碼進(jìn)行計(jì)數(shù),并采取相應(yīng)的行動(dòng)。 wuu28資訊網(wǎng)——每日最新資訊28at.com

所以一個(gè)Semaphore 信號(hào)量有且僅有 3 種操作,且它們?nèi)渴窃拥摹?wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 初始化許可集、增加許可、獲取許可。
  • 增加許可,release()方法釋放一個(gè)阻塞,增加一個(gè)許可。
  • 獲取許可,acquire()方法獲取許可,再獲取許可前處于阻塞等待。

方法wuu28資訊網(wǎng)——每日最新資訊28at.com

說明wuu28資訊網(wǎng)——每日最新資訊28at.com

Semaphore(int permits)wuu28資訊網(wǎng)——每日最新資訊28at.com

permits是允許同時(shí)運(yùn)行的線程數(shù)目創(chuàng)建指定數(shù)據(jù)線程的信號(hào)量wuu28資訊網(wǎng)——每日最新資訊28at.com

Semaphore(int permits, boolean fair)wuu28資訊網(wǎng)——每日最新資訊28at.com

permits是允許同時(shí)運(yùn)行的線程數(shù)目,創(chuàng)建指定數(shù)據(jù)線程的信號(hào)量;fair指定是公平模式還是非公平模式,默認(rèn)非公平模式wuu28資訊網(wǎng)——每日最新資訊28at.com

void acquire()wuu28資訊網(wǎng)——每日最新資訊28at.com

方法阻塞,直到申請(qǐng)獲取到許可證才可以運(yùn)行當(dāng)前線程wuu28資訊網(wǎng)——每日最新資訊28at.com

void release()wuu28資訊網(wǎng)——每日最新資訊28at.com

釋放當(dāng)前線程一個(gè)阻塞的 acquire() 方法,方法增加一個(gè)許可證wuu28資訊網(wǎng)——每日最新資訊28at.com

intavailablePermits()wuu28資訊網(wǎng)——每日最新資訊28at.com

返回此信號(hào)量中當(dāng)前可用的許可證數(shù)wuu28資訊網(wǎng)——每日最新資訊28at.com

intgetQueueLength()wuu28資訊網(wǎng)——每日最新資訊28at.com

返回正在等待獲取許可證的線程數(shù)wuu28資訊網(wǎng)——每日最新資訊28at.com

booleanhasQueuedThreads()wuu28資訊網(wǎng)——每日最新資訊28at.com

是否有線程正在等待獲取許可證wuu28資訊網(wǎng)——每日最新資訊28at.com

void reducePermits(int reduction)wuu28資訊網(wǎng)——每日最新資訊28at.com

減少reduction個(gè)許可證,是個(gè)protected方法wuu28資訊網(wǎng)——每日最新資訊28at.com

Collection getQueuedThreads()wuu28資訊網(wǎng)——每日最新資訊28at.com

返回所有等待獲取許可證的線程集合,是個(gè)protected方法wuu28資訊網(wǎng)——每日最新資訊28at.com

代碼:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;import java.util.concurrent.Semaphore;import java.util.concurrent.TimeUnit;public class Test18 {    public static void main(String[] args) {        // 1. 創(chuàng)建信號(hào)量對(duì)象控制并發(fā)線程數(shù)量,設(shè)置許可數(shù)5個(gè)(同時(shí)運(yùn)行5個(gè)線程)        Semaphore semaphore = new Semaphore(5, true);        // 2. 循環(huán)運(yùn)行10個(gè)線程(會(huì)看到每次只允許5個(gè)線程)        for (int i = 0; i < 10; i++) {            new Thread(() -> {                try {                    // 2.1 申請(qǐng)獲取許可                    semaphore.acquire();                    // 2.2 運(yùn)行業(yè)務(wù)                    System.out.println(Thread.currentThread().getName() + "車,進(jìn)入停車場");                    TimeUnit.SECONDS.sleep(3);// 讓當(dāng)前線程休眠(讓線程多運(yùn)行一會(huì),方便觀察效果)                    System.out.println(Thread.currentThread().getName() + "車,離開停車場");                    // 2.3 釋放阻塞,增加一個(gè)許可(讓下一個(gè)阻塞的線程運(yùn)行)                    semaphore.release();                } catch (Exception e) {                    e.printStackTrace();                }            }).start();        }    }}

運(yùn)行效果wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

特點(diǎn)wuu28資訊網(wǎng)——每日最新資訊28at.com

Semaphore 在計(jì)數(shù)器不為 0 的時(shí)候?qū)€程就放行,一旦達(dá)到 0,那么所有請(qǐng)求資源的新線程都會(huì)被阻塞,包括增加請(qǐng)求到許可的線程,Semaphore 是不可重入的。 wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 每一次請(qǐng)求一個(gè)許可都會(huì)導(dǎo)致計(jì)數(shù)器減少 1,同樣每次釋放一個(gè)許可都會(huì)導(dǎo)致計(jì)數(shù)器增加 1,一旦達(dá)到 0,新的許可請(qǐng)求線程將被掛起。

Semaphore 有兩種模式,公平模式 和 非公平模式(默認(rèn)使用)wuu28資訊網(wǎng)——每日最新資訊28at.com

  • 公平模式就是調(diào)用 acquire 的順序就是獲取許可證的順序,遵循 FIFO。
Semaphore semaphore = new Semaphore(許可數(shù), true); // 公平模式
  • 非公平模式是搶占式的,也就是有可能一個(gè)新的獲取線程恰好在一個(gè)許可證釋放時(shí)得到了這個(gè)許可證,而前面還有等待的線程。
Semaphore semaphore = new Semaphore(許可數(shù), false); // 非公平模式,(默認(rèn))

小結(jié)wuu28資訊網(wǎng)——每日最新資訊28at.com

  • Semaphore 的使用(3個(gè)操作)

     – 初始化許可集wuu28資訊網(wǎng)——每日最新資訊28at.com

     – 增加許可,release()方法釋放一個(gè)阻塞,增加一個(gè)許可。wuu28資訊網(wǎng)——每日最新資訊28at.com

     – 獲取許可,acquire()方法獲取許可,再獲取許可前處于阻塞等待。wuu28資訊網(wǎng)——每日最新資訊28at.com

20、并發(fā)工具類:Exchanger(交換數(shù)據(jù))

目標(biāo):掌握Exchanger的使用wuu28資訊網(wǎng)——每日最新資訊28at.com

介紹wuu28資訊網(wǎng)——每日最新資訊28at.com

Exchanger(交換者)是一個(gè)用于線程間協(xié)作的工具類,可以用于進(jìn)行線程間的數(shù)據(jù)交換。wuu28資訊網(wǎng)——每日最新資訊28at.com

交換數(shù)據(jù)原理wuu28資訊網(wǎng)——每日最新資訊28at.com

  • Exchanger提供一個(gè)同步點(diǎn),在這個(gè)同步點(diǎn)兩個(gè)線程可以交換彼此的數(shù)據(jù)。
  • 兩個(gè)線程通過 exchange() 方法交換數(shù)據(jù)。
  • 第一個(gè)線程先執(zhí)行 exchange() 方法,會(huì)一直等待第二個(gè)線程也執(zhí)行exchange()到達(dá)同步點(diǎn)時(shí),兩個(gè)線程交換數(shù)據(jù),將本線程生產(chǎn)出來的數(shù)據(jù)傳遞給對(duì)方。
  • 使用 Exchanger 的重點(diǎn)是用對(duì)的線程使用 exchange() 方法。

wuu28資訊網(wǎng)——每日最新資訊28at.com

API方法wuu28資訊網(wǎng)——每日最新資訊28at.com

方法wuu28資訊網(wǎng)——每日最新資訊28at.com

說明wuu28資訊網(wǎng)——每日最新資訊28at.com

V exchange(V x)wuu28資訊網(wǎng)——每日最新資訊28at.com

用于進(jìn)行線程間的數(shù)據(jù)交換wuu28資訊網(wǎng)——每日最新資訊28at.com

V exchange(V x, long timeout, TimeUnit unit)wuu28資訊網(wǎng)——每日最新資訊28at.com

設(shè)置交換數(shù)據(jù)并等待超時(shí)時(shí)間wuu28資訊網(wǎng)——每日最新資訊28at.com

代碼:wuu28資訊網(wǎng)——每日最新資訊28at.com

package cn.itcast.thread;import java.util.concurrent.Exchanger;public class Test19 {    public static void main(String[] args) {        // 1. 創(chuàng)建交換數(shù)據(jù)對(duì)象,并設(shè)置傳輸數(shù)據(jù)的類型        Exchanger<String> exchanger = new Exchanger<>();        // 2. 啟動(dòng)2個(gè)線程進(jìn)行交換數(shù)據(jù)        // 創(chuàng)建線程1        new Thread(() -> {            // 2.1 定義交換的數(shù)據(jù)            String girl1 = "【柳巖】";            System.out.println(Thread.currentThread().getName() + "說:我的女友 " + girl1);            System.out.println(Thread.currentThread().getName() + "說:等待線程2交換數(shù)據(jù)");            // 2.2 將數(shù)據(jù)交換給線程2,并拿到線程2的數(shù)據(jù)            try {                String b = exchanger.exchange(girl1);//注意:如果線程2沒有到達(dá)同步點(diǎn),當(dāng)前線程會(huì)被阻塞一直等到                //成功獲取線程2的數(shù)據(jù)后                System.out.println(Thread.currentThread().getName() + "說:我拿到了 " + b);            } catch (InterruptedException e) {                e.printStackTrace();            }        }).start();        // 創(chuàng)建線程2        new Thread(()->{            // 2.3 定義交換的數(shù)據(jù)            String girl2 = "【楊冪】";            System.out.println(Thread.currentThread().getName()+"說:我的女友 " + girl2);            System.out.println(Thread.currentThread().getName()+"說:等待線程1交換數(shù)據(jù)");            // 2.4 將數(shù)據(jù)交換給線程1,并拿到線程1的數(shù)據(jù)            try {                String a = exchanger.exchange(girl2);                // 成功獲取線程1的數(shù)據(jù)后                System.out.println(Thread.currentThread().getName()+"說:我拿到了 " + a);            } catch (InterruptedException e) {                e.printStackTrace();            }        }).start();    }}

運(yùn)行效果wuu28資訊網(wǎng)——每日最新資訊28at.com

wuu28資訊網(wǎng)——每日最新資訊28at.com

注意:如果兩個(gè)線程有一個(gè)沒有執(zhí)行exchange()方法,則會(huì)一直等待,如果擔(dān)心有特殊情況發(fā)生,避免一直等待,可以使用exchange(V x,longtimeout,TimeUnit unit)設(shè)置最大等待時(shí)長。wuu28資訊網(wǎng)——每日最新資訊28at.com

21、總結(jié)

整篇文章內(nèi)容較多,這里做個(gè)總結(jié)。1~7小節(jié)講了并發(fā)編程的核心基礎(chǔ)概念;在對(duì)并發(fā)有了一定的基礎(chǔ)了解后,8~9小節(jié)講了JVM對(duì)并發(fā)問題的設(shè)計(jì)——JMM;從10小節(jié)開始詳細(xì)介紹了JDK 并發(fā)包里面的常用工具。這里只是一個(gè)入門級(jí)別的了解,但是萬丈高樓平地起,這些基礎(chǔ)是后面提升必不可缺的知識(shí),希望大家可以掌握它。wuu28資訊網(wǎng)——每日最新資訊28at.com

作者介紹

蔡柱梁,51CTO社區(qū)編輯,從事Java后端開發(fā)8年,做過傳統(tǒng)項(xiàng)目廣電BOSS系統(tǒng),后投身互聯(lián)網(wǎng)電商,負(fù)責(zé)過訂單,TMS,中間件等。wuu28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-51249-0.html看完后,你再也不用怕面試問并發(fā)編程啦

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: useEffect 實(shí)踐案例:自定義 Hook

下一篇: JS問題:項(xiàng)目中如何區(qū)分使用防抖或節(jié)流?

標(biāo)簽:
  • 熱門焦點(diǎn)
Top 主站蜘蛛池模板: 蕉岭县| 漯河市| 平阳县| 桂林市| 龙井市| 社会| 襄汾县| 巨鹿县| 甘德县| 金华市| 长垣县| 喀喇沁旗| 芮城县| 泸州市| 枝江市| 满城县| 罗甸县| 赣榆县| 临泽县| 呼玛县| 潞西市| 视频| 林州市| 东城区| 白山市| 桃江县| 沾益县| 柳江县| 浦北县| 重庆市| 离岛区| 湘西| 鄯善县| 图木舒克市| 樟树市| 太仓市| 松江区| 久治县| 商丘市| 梁山县| 且末县|