6個併發問題引發的血案
1、synchronized的CPU原語級別是如何實現的?
2、一千萬個數,如何高效求和。
3、不用數學庫,求2開平方的值,精確到小數點兒後10位。
4、線程A不斷打印1-10的數字,打印到第5個數字時通知線程B,請完成編碼。
5、下列三種業務,應該如何使用線程池:
高併發、任務執行時間短的業務
併發不高、任務執行時間長的業務
併發高、業務執行時間長的業務
6、你如何來設計12306網站,能夠撐住最高百萬級別TPS(淘寶最高54萬TPS)?
以上6 個併發問題,一個都答不上來?強烈建議來看看這本馬士兵的「多線程與高併發」書籍
給大家看看這本書籍的大概內容
目錄:
- 第一節:線程的基本概念
- 第二節:volatile與CAS
- 第三節:Atomic類和線程同步新機制
- 第四節:LockSupport、淘寶面試題與源碼閱讀方法論
- 第五節:AQS源碼閱讀與強軟弱虛4種引用以及ThreadLocal原理與源碼
- 第六節:併發容器
- 第七節:線程池
- 第八節:線程池與源碼閱讀
- 第九節:JMH與Disruptor
第一節:線程的基本概念
線程與高併發大概講六大塊
- 第一:基本的概念,從什麼是線程開始
- 第二:JUC同步工具,就是各種同步鎖
- 第三:同步容器
- 第四:線程池
- 第五:高頻面試加分項的一些面試用的東西,包括纖程
- 第六:Disruptor,不知道有多少同學聽說過這個框架的,這個框架它也是一個MQ框架
- (Message Queue)叫做消息隊列,消息隊列非常多,後面還會給大家講Kafka、RabbitMQ,
- Redis等這些都是消息隊列。Disruptor是目前大家公認的在單機環境上效率最高的、性能最快的
- MQ。
我們先說一下為什麼要講多線程和高併發?
- 原因是,你想拿到一個更高的薪水,在面試的時候呈現出了兩個方向的現象:
- 第一個是上天
- 項目經驗
- 高併發 緩存 大流量 大數據量的架構設計
- 第二個是入地
- 各種基礎算法,各種基礎的數據結構
- JVM OS 線程 IO等內容
- 多線程和高併發,就是入地裡面的內容。
基本概念
我們先從線程的基本概念開始,給大家複習一下,不知道有多少同學是基礎不太好,說什麼是線程都不
知道的,如果這樣的話,花時間去補初級內容的課
第二節:volatile與CAS
volatile
我們先來看這個volatile的概念,volatile它是什麼意思,現在像大的互聯網企業的面試,基本上volatile
是必會的,有時候他也不會太問,認為你應該會,但是中小企業也就開始問這方面的問題。
我們來看一下這個小程序,寫了一個方法啊,首先定義了一個變量布爾類型等於true,這裡模擬的是一
個服務器的操作,我的值為true你就給我不間斷的運行,什麼時候為false你再停止。 測試new Thread
啟動一個線程,調用m方法,睡了一秒,最後running等於false,運行方法他是不會停止的。 如果你要
把volatile打開,那麼結果就是啟動程序一秒之後他就會m end停止。(volatile就是不停的追蹤這個
值,時刻看什麼時候發生了變化)
CAS
cas號稱是無鎖優化,或者叫自旋。這個名字無所謂,理解它是幹什麼的就行,概念這個東西是人為了
描述問題解決問題而定義出來的,所以怎麼定義不是很重要,重點是在解決問題上
我們通過Atomic類(原子的)。由於某一些特別常見的操作,老是來回的加鎖,加鎖的情況特別多,所
以乾脆java就提供了這些常見的操作這麼一些個類,這些類的內部就自動帶了鎖,當然這些鎖的實現並
不是synchronized重量級鎖,而是CAS的操作來實現的(號稱無鎖)。
我們來舉例幾個簡單的例子,凡是以Atomic開頭的都是用CAS這種操作來保證線程安全的這麼一些個
類。AtomicInteger的意思就是裡面包了一個Int類型,這個int類型的自增 count++ 是線程安全的,還有
拿值等等是線程安全的,由於我們在工作開發中經常性的有那種需求,一個值所有的線程共同訪問它往
上遞增 ,所以jdk專門提供了這樣的一些類。使用方法AtomicInteger如下代碼
<code>**/<code>
<code>* 解決同樣的問題的高效方法,使用AtomXXX類/<code>
<code>* AtomXXX類的本身方法都是原子性的,但不能保證多個方法連續調用都是原子性的/<code>
<code>* @author mashibing/<code>
<code>*//<code>
<code>package com.mashibing.juc.c_018_00_AtomicXXX;/<code>
<code>import java.util.ArrayList;/<code>
<code>import java.util.List;/<code>
<code>import java.util.concurrent.atomic.AtomicInteger;/<code>
<code>好,我們來分許分析,它的內部實現的原理,主要是聊原理,它的用法看看API都會用。這個原理叫/<code>
<code>CAS操作,incrementAndGet() 調用了getAndAddInt/<code>
<code>當然這個也是一個CompareAndSetInt操作/<code>
<code>public class T01_AtomicInteger {/<code>
<code>/*volatile*/ //int count1 = 0;/<code>
<code>AtomicInteger count = new AtomicInteger(0);/<code>
<code>/*synchronized*/ void m() {/<code>
<code>for (int i = 0; i < 10000; i++)/<code>
<code>//if count1.get() < 1000/<code>
<code>count.incrementAndGet(); //count1++/<code>
<code>}/<code>
<code>public static void main(String[] args) {/<code>
<code>T01_AtomicInteger t = new T01_AtomicInteger();/<code>
<code>List<thread> threads = new ArrayList<thread>();/<thread>/<thread>/<code>
<code>for (int i = 0; i < 10; i++) {/<code>
<code>threads.add(new Thread(t::m, "thread-" + i));/<code>
<code>}/<code>
<code>threads.forEach((o) -> o.start());/<code>
<code>threads.forEach((o) -> {/<code>
<code>try {/<code>
<code>o.join();/<code>
<code>} catch (InterruptedException e) {/<code>
<code>e.printStackTrace();/<code>
<code>}/<code>
<code>});/<code>
<code>System.out.println(t.count);/<code>
<code>}/<code>
<code>}/<code>
第三節:Atomic類和線程同步新機制
今天,我們繼續講一個Atomic的問題,然後開始講除synchronized之外的別的鎖。在前面內容我們講
了synchronized、volatile、Atomic和CAS,Atomic我們只是講了一個開頭還沒有講完,今天我們繼
續。
像原來我們寫m++你得加鎖,在多線程訪問的情況下,那現在我們可以用AtomicInteger了,它內部就
已經幫我們實現了原子操作,直接寫 count.incrementAndGet(); //count1++ 這個就相當於count++。原
來我們對count是需要加鎖的,現在就不需要加鎖了。
我們看下面小程序,模擬,我們計一個數,所有的線程都要共同訪問這個數count值,大家知道如果所
有線程都要訪問這個數的時候,如果每個線程給它往上加了10000,你這個時候是需要加鎖的,不加鎖
會出問題。但是,你把它改成AtomicInteger之後就不用在做加鎖的操作了,因為incrementAndGet內
部用了cas操作,直接無鎖的操作往上遞增,有同學可能會講為什麼要用無鎖操作啊,原因是無鎖的操
作效率會更高。
第四節:LockSupport、淘寶面試題與源碼閱讀方法論
首先我們簡單回顧一下前面三節課講的內容,分別有線程的基本概念、synchronized、volatile、
AtomicXXX、各種JUC同步框架(ReentrantLock、CountDownLatch、CyclicBarrier、Phaser、
ReadWriteLock-StampedLock、Semaphore、Exchanger、LockSupport),其中synchornized重點講
了一下,包括有synchornized的底層實現原理、鎖升級的概念(四種狀態:無鎖、偏向鎖、輕量級鎖、
重量級鎖),volatile我們講了可見性和禁止指令重排序如何實現。
synchronized和ReentrantLock的不同?
synchronized:系統自帶、系統自動加鎖,自動解鎖、不可以出現多個不同的等待隊列、默認進行
四種鎖狀態的升級
ReentrantLock:需要手動枷鎖,手動解鎖、可以出現多個不同的等待隊列、CIS的實現
本章我們補一個小漏洞,它叫LockSupport,然後我們分析兩道面試題,緊接著我會教大家閱讀源碼的
技巧,源碼層出不窮,生生不息,掌握了源碼的閱讀技巧,大家培養出了閱讀源碼興趣的時候,之後好
多代碼,你需要自己去摳,摳出來才是你自己的,最後我們會分析AQS源碼,以上是我們本章主講的內
容概述。
LockSupport
我們會以幾個小程序為案例,展開對LockSupport的講解,在以前我們要阻塞和喚醒某一個具體的線程
有很多限制比如:
1、因為wait()方法需要釋放鎖,所以必須在synchronized中使用,否則會拋出異常
IllegalMonitorStateException
2、notify()方法也必須在synchronized中使用,並且應該指定對象
3、synchronized()、wait()、notify()對象必須一致,一個synchronized()代碼塊中只能有一個線程調
用wait()或notify()
以上諸多限制,體現出了很多的不足,所以LockSupport的好處就體現出來了。
在JDK1.6中的java.util.concurrent的子包locks中引了LockSupport這個API,LockSupport是一個比較
底層的工具類,用來創建鎖和其他同步工具類的基本線程阻塞原語。java鎖和同步器框架的核心 AQS:
AbstractQueuedSynchronizer,就是通過調用 LockSupport .park()和 LockSupport .unpark()的方
法,來實現線程的阻塞和喚醒的。我們先來看一個小程序:
後續的內容給大家大概截個圖,由於內容太多了
第五節:AQS源碼閱讀與強軟弱虛4種引用以及ThreadLocal原理與源碼
第六節:併發容器
今天是第六天了,這節課本來想上一個大而全的課,後來發現這個實在目標太大了,大而全的概念就是
上節課講到的那張容器圖中的每一個都講的非常的細緻,然後去談他們的源碼。但是如果這麼講的話我
們高併發的課就講不完了,所以也彆著急,後面單獨開一門課來講集合,集合的發展歷程,現在為什麼
講這個併發容器呢,主要是為了線程池做準備,線程池裡有一個參數就是用併發容器來做你工作任務的
容器。
第七節:線程池
第八節:線程池與源碼閱讀
第九節:JMH與Disruptor
如何獲取?
轉發這篇文章,關注我,私信回覆“馬士兵”即可獲取電子書籍,以上 spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化、分佈式架構
如何私信?
關注我後,在手機,點進我的主頁,主頁上方右上角有個私信,點擊私信,如何回覆關鍵字“馬士兵”即可
粉絲福利:
以上6 個併發問題,一個都答不上來?強烈建議!!拿出4個小時的時間,參加一次馬士兵老師的《多線程與高併發》訓練營。
閱讀更多 java互聯網高級架構 的文章