多線程-----學(xué)習(xí)筆記,你知道多少?
多線程理解學(xué)習(xí):
多線程
百度百科:多線程(multithreading),是指從軟件或硬件上實現(xiàn)多個線程并發(fā)執(zhí)行的請求具有多線程能力的計算機因有硬件支持而能夠在同一時間執(zhí)行多于一個線程,進而提升整體處理性能。具有這種能力的系統(tǒng)包括對稱多處理機、多核心處理器以及芯片級多處理或同時多線程處理器。在一個程序中,這些獨立運行的程序片段叫做“線程”(Thread),利用它編程的概念就叫做“多線程處理”。
進程與線程區(qū)別:
進程:一個程序就是一個進程例如我正在運行的微信,網(wǎng)易云等都屬于一個進程。
線程:線程是運行在計算機操作系統(tǒng)上運算調(diào)度最小的單位,它包含在我們的進程中,在統(tǒng)一進程中線程擁有該進程的全部系統(tǒng)資源。
并行與并發(fā):
舉個例子:
例1:
你吃飯吃到一半,電話來了,你一直到吃完了以后才去接,這就說明你不支持并發(fā)也不支持并行。
你吃飯吃到一半,電話來了,你停了下來接了電話,接完后繼續(xù)吃飯,這說明你支持并發(fā)。 (不一定是同時的)
你吃飯吃到一半,電話來了,你一邊打電話一邊吃飯,這說明你支持并行。
例2:
你在打游戲,女朋友突然打來了視頻電話,如果你無視女朋友的電話,一直到游戲結(jié)束才回她電話,這就說明你不支持并發(fā),也不支持并行。
你在打游戲中,女票來了電話,你趕緊接了電話,放下了手頭的游戲,等你女票電話結(jié)束你才繼續(xù)玩你的游戲,這說明你支持并發(fā)。(不一定是同時的)
你玩游戲玩到一半,女票來了電話,你一邊接電話一邊打你的游戲說明你支持并行。
并發(fā)的關(guān)鍵是你有處理多個任務(wù)的能力,不一定要同時;
并行的關(guān)鍵是你有同時處理多個任務(wù)的能力。
所以我認為它們最關(guān)鍵的點就是:是否是『同時』。并發(fā)是輪流處理多個任務(wù),并行是同時處理多個任務(wù)
線程池:
線程的創(chuàng)建方式:Java就是一個天生的多線程語言。在我們運行main方法的時候,其實就是創(chuàng)建并啟動了一個main線程。
線程的狀態(tài)(五種):
1.新建新創(chuàng)建一個線程。
2.就緒:線程對象創(chuàng)建后,其他線程(比如main線程)調(diào)用了該對象的start( )方法。該狀態(tài)的線程位于可運行線程池中,等待被線程調(diào)度選中,獲取CPU的使用權(quán)。
3.運行:獲取了CPU的使用權(quán)。
4.阻塞:線程可能被掛起,或者被中斷,讓出CPU的使用權(quán)。
5.死亡:線程run()、main()方法執(zhí)行結(jié)束,或者因異常退出了run()方法。則該線程結(jié)束生命周期。死亡的線程不可再次復(fù)生。

在Java中創(chuàng)建線程有三種方式:
1.通過集成Thread類,重寫run()方法創(chuàng)建線程
public class Test extends Thread{
@Override
public void run(){
System.out .println("創(chuàng)建線程");
System.out.println(getName());
}
public static void main(){
Test test = new Test();
test.setName("線程A");
test.start();
System.out.println(Thread.currentThread().getName());
}
}
輸出結(jié)果:
main
創(chuàng)建線程
線程A
2.通過實現(xiàn)Runnable接口創(chuàng)建線程
public class Test implements Runnable{
@Override
public void run(){
System.out.println("創(chuàng)建線程");
}
public static void main(){
Test test = new Test();
Thread thread = new Thread(test);
thread.start();
}
}
創(chuàng)建線程
3.通過實現(xiàn)Callable接口和Future創(chuàng)建線程,該方式需要通過FutureTask幫助獲取返回值。
public class Test implements Callable<String>{
@Override
public Stirng call()throws Exception{
System.out .println("創(chuàng)建線程");
return"返回結(jié)果";
}
public static void main(String args[]){
Test test = new Test();
FutureTask<String> task = new FutureTask<String>(test);
Thread thread = new Thread(task);
thread.start();
try{
System.out.println(task.get());
}catch(InterruptedException e){
e.printStackTrace
}catch(ExecutionException e){
e.printStackTrace();
}
}
}
創(chuàng)建線程
返回結(jié)果
為什么需要線城池?
線程的創(chuàng)建和銷毀都會消耗系統(tǒng)的資源,將線程放在一個緩存池,需要使用時直接從緩存池中獲取線程,通過重用使用已創(chuàng)建的線程來降低系統(tǒng)所消耗的能源。
線程池的使用:
在Executor類中有4種線程池的創(chuàng)建方法。
1.newCachedThreadPool():創(chuàng)建一個緩存線程池,創(chuàng)建線程數(shù)量不限制,線程長時間未使用會被回收,如果有大量線程同時運行可能會導(dǎo)致系統(tǒng)癱瘓。SynchronousQueue(同步隊列):內(nèi)部只能包含一個元素的隊列。
public static ExecutorService newCachedThreadPool(){
return new ThreadPoolExecutor(0,Integer.MAX_VALUE,
60L,TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}
2.newFixedThreadPool():創(chuàng)建固定線程的線程池,LinkedBlockingQueue隊列中大小是不限的,所有可能會出現(xiàn)內(nèi)存溢出的情況。
pulic static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runable>());
}
3.newSIngleTreadExecutor()創(chuàng)建一個只有一個線程的線程池,俗稱單例線程池同樣也是采用的LinkedBlockingQueue.
public static ExecutorService newSingleThreadExecutor(){
return new FinalizableDelegatedExcutorService(1,1,0L,
TimeUnit.MILLISECONDS,new (LinkedBlockingQueue<Runnable>()));
}
4.newScheduledThreadPool():創(chuàng)建一個定長的線程池,而且支持定時的以及周期性的任務(wù)執(zhí)行,支持定時及周期性任務(wù)執(zhí)行。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
return new ScheduledThreadPoolExecutor(corePoolSize);
}
線程池使用注意事項:
阿里開發(fā)規(guī)范(原文):
【強制】線程池不允許使用 Executors 去創(chuàng)建,而是通過 ThreadPoolExecutor 的方式,這
樣的處理方式讓寫的同學(xué)更加明確線程池的運行規(guī)則,規(guī)避資源耗盡的風(fēng)險。
說明:Executors 返回的線程池對象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:
允許的請求隊列長度為 Integer.MAX_VALUE,可能會堆積大量的請求,從而導(dǎo)致 OOM。
2) CachedThreadPool:
允許的創(chuàng)建線程數(shù)量為 Integer.MAX_VALUE,可能會創(chuàng)建大量的線程,從而導(dǎo)致 OOM。
ThreadPoolExecutor:
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,
long KeepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory){
this(corePoolSize,maximumPoolSize,long keepAliveTime,unit.workQueue,threadFactory,defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
? int maximunmPoolSize,
? long keepAliveTime,
? TimeUnit unit,
? BlockingQueue<Runnable> workQueue,
? ThreadFactory threadFacyory,
? RejectedExecutionHandler handler){
if(corePoolSize < 0 ||
? ?maximumPoolSize<=0 ||
? ?maximumPoolSize < corePoolSize ||
? ?keepAliveTime < 0)
? ?throw new IllegalArgumentException();
if(workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(KeepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
參數(shù)說明:
corePoolSize:核心線程數(shù)
maximumPoolSize:最大線程數(shù)
keepAliveTime:線程保持活躍時間
unit:線程活躍時間的單位
workQueue:任務(wù)隊列
threadFactory:線程工廠,線程創(chuàng)建的方式
handler:拒絕策略
TimeUnit類,配合keepAliveTime使用指定時間格式
納秒 NANOSECONDS
微秒 MICROSECONDS
毫秒 MILLISECONDS
秒 SECONDS
分 MINUTES
時 HOURS
天 DAYS
拒絕策略
RejectedExecutionHandler:ThreadPoolExecutor提供了四種拒絕策略
1.AbortPolicy(默認使用):拋出異常
public stastic class AbortPolicy implements RejectedExecutionHabdler{
public AbortPolicy(){
public void rejectedExecution(Runnable r,ThreadPoolExecutor e){
throw new RejectedExecutionException("Task"+r.toString()+"rejected from"+e.toString())
}
}
]
2.CallerRunsPolicy:在當(dāng)前線程運行該任務(wù)
public static class CallerRunPolicy implements? RejectedExecutor{
public CallerRunPolicy(){}
public void rejectedExecution(Runnable r,ThreadPoolExecutor e){
if(le.isShutdown()){
r.run();
}
}
}
3.DiscardPolicy:丟棄任務(wù)
public static class DiscardPolicy implements RejectedExecutionHandler{
public DiscardPolicy(){ }
public void rejectedExecution(Runnable r,ThreadPoolExecutor e){
}
}
4.DiscardOldestPolicy:丟棄最早的任務(wù)
public static class DiscardOldestPolicy{
public DiscardOldestPolicy(){ }
public void rejectedExecution(Runnable r,ThreadPoolExecutor e){
if(!e.isShutdown()){
e.getQueue().poll();
e.executor(r);
}
}
}
**線程池的實現(xiàn)原理:**當(dāng)有新任務(wù)時,會創(chuàng)建線程來執(zhí)行任務(wù),當(dāng)線程數(shù)達到corePoolSize時,就會將任務(wù)放在阻塞隊列,當(dāng)阻塞隊列滿了,并且線程數(shù)達到了maximumPoolSize時,會觸發(fā)拒絕策略拒絕任務(wù)。

鎖:
**什么時候需要用到鎖:**多線程的環(huán)境下肯定會出現(xiàn)線程安全的問題,通過鎖可以解決線程的安全問題,保證數(shù)據(jù)的一致性。
鎖升級:
鎖狀態(tài):偏向鎖,輕量級鎖,重量級鎖(級別由低到高)
1. 偏向鎖:
大多數(shù)情況下,鎖不僅不存在多線程競爭,而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價更低而引入了偏向鎖。當(dāng)一個線程訪問同步塊并獲取鎖時,會在對象頭和棧幀中的鎖記錄里存儲鎖偏向的線程ID,以后該線程在進入和退出同步塊時不需要進行CAS操作來加鎖和解鎖,只需簡單地測試一下對象頭的Mark Word里是否存儲著指向當(dāng)前線程的偏向鎖。如果測試成功,表示線程已經(jīng)獲得了鎖。如果測試失敗,則需要再測試一下Mark Word中偏向鎖的標(biāo)識是否設(shè)置成1(表示當(dāng)前是偏向鎖):如果沒有設(shè)置,則使用CAS競爭鎖;如果設(shè)置了,則嘗試使用CAS將對象頭的偏向鎖指向當(dāng)前線程。
2. 輕量級鎖:
線程在執(zhí)行同步塊之前,JVM會先在當(dāng)前線程的棧楨中創(chuàng)建用于存儲鎖記錄的空間,并將對象頭中的Mark Word復(fù)制到鎖記錄中,官方稱為Displaced MarkWord。然后線程嘗試使用CAS將對象頭中的Mark Word替換為指向鎖記錄的指針。如果成功,當(dāng)前線程獲得鎖,如果失敗,表示其他線程競爭鎖,當(dāng)前線程便嘗試使用自旋來獲取鎖。
3. 輕量級解鎖時,會使用原子的CAS操作將Displaced Mark Word替換回到對象頭,如果成功,則表示沒有競爭發(fā)生。如果失敗,表示當(dāng)前鎖存在競爭,鎖就會膨脹成重量級鎖。一旦鎖升級成重量級鎖,就不會再恢復(fù)到輕量級鎖狀態(tài)。
volatile:
Java中的關(guān)鍵字,基于jvm實現(xiàn)。可以保證被他修飾的方法或是代碼塊在任意一個時刻只能有一個線程執(zhí)行。
synchronized實現(xiàn)原理
public class Test(){
public static void main(String[] args){
synchronized(Object.class){
system.out.println("synchronized");
}
}
}
synchronized使用方式
Java中所有對象都可以被當(dāng)做synchronized的鎖。
1.synchronized使用在普通方法中,鎖是當(dāng)前對象,進入被synchronized修飾的普通方法時要獲取當(dāng)前對象的鎖。
public static void main(String [] args){
Test test = new Test();
new Thread(test::methodA).start();
new Thread(test::methodB).start();
}
public synchronized void methodA(){
try{
Thread.sleep(2000);
}catch(InterrupedException e){
e.printStackTrace();
}
System.out.println("methodA");
}
public synchronized void methodB(){
System.out.println("methodB");
}
methodA
methodB
3.synchronized使用在代碼塊中,鎖是代碼塊指定對象的鎖,進入被synchronized修飾的代碼塊時需要獲取到括號中對象的鎖。
public Objectnlock = new Object();
public static void main(String[] args){
Test test = new Test();
new Thread(test::methodA).start();
new Thread(test::methodB).start();
}
public void method(){
synchronized(lock){
try{
Thread.sleep(2000);
}catch(InterrptedExcption e){
e.printStackTrace();
}
System.out.println("methodA");
}
}
public void methodB(){
synchronied(lock){
System.out println("methodB")
}
}
methodA
methodB
Lock鎖
在Lock接口出現(xiàn)之前,只能靠synchronized關(guān)鍵字實現(xiàn)鎖功能的,在JDK1.5后,并發(fā)包中新增了Lock接口(以及相關(guān)實現(xiàn)類)用來實現(xiàn)鎖功能,它提供了與synchronized關(guān)鍵字類似的同步功能,并且讓我們可以比synchronized為靈活的運行性鎖。自己管理鎖則需要手動獲取鎖和釋放鎖,使用不當(dāng)就會造成系統(tǒng)癱瘓,比如死鎖。
//獲取鎖
void lock();
//當(dāng)前線程未被中斷,則獲取鎖
void lockInterruptibly() throws InterruptedException;
//嘗試獲取
boolean tryLock();
//限定時間內(nèi)獲取,超時則拋異常
boolean tryLock(Long time,TimeUnit) throws InterruptedException;
//釋放鎖
void unlock();
CAS(Compare and Swap)
Compare and Swap:比較并替換,CAS是區(qū)別于synchronize的一種樂觀鎖。CAS是一種無鎖算法,CAS有3個操作數(shù),內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的新值B。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時,將內(nèi)存值V修改為B,否則什么都不做。在sun.misc包下有個被final關(guān)鍵字修飾的Unsafe的類,該類不對外提供。該類中的方法都被native關(guān)鍵字修飾,表示該方法由本地實現(xiàn),Unsafe底層均是采用的C++語言實現(xiàn)的,保證在cpu上運行的原子性。Unsafe類只提供了三種原子操作方法,其他類型都是轉(zhuǎn)成這三種類型再使用對應(yīng)的方法去原子更新的。
AQS(AbstractQueuedSynchronizer)
AQS全稱:AbstractQueuedSynchronizer是一個同步隊列(它是一個抽象類),AQS底層使用了模板方法模式實現(xiàn)了對同步狀態(tài)的管理,對阻塞線程進行排隊,等待通知等等。AQS的核心也包括了這些方面:同步隊列,獨占鎖的獲取和釋放,共享鎖的獲取和釋放以及可中斷鎖,超時等待鎖獲取這些特性的實現(xiàn)。AbstractQueuedSynchronizer是一個FIFO(First Input First Output)即先進先出的隊列
static final class Node {
// 標(biāo)記共享鎖
? ? ? ? static final Node SHARED = new Node();
// 標(biāo)記排它鎖
? ? ? ? static final Node EXCLUSIVE = null;
// 節(jié)點從同步隊列取消
? ? ? ? static final int CANCELLED =? 1;
//后繼節(jié)點的線程處于等待狀態(tài),如果當(dāng)前節(jié)點釋放同步狀態(tài)會通知后繼節(jié)點,使得后繼節(jié)點的線程能夠運行;
? ? ? ? static final int SIGNAL? ? = -1;
// 當(dāng)前節(jié)點進入等待隊列中
? ? ? ? static final int CONDITION = -2;
// 表示下一次共享式同步狀態(tài)獲取將會無條件傳播下去
? ? ? ? static final int PROPAGATE = -3;
// 線程狀態(tài)
? ? ? ? volatile int waitStatus;
// 前節(jié)點
? ? ? ? volatile Node prev;
? // 后節(jié)點
? ? ? ? volatile Node next;
? ? ? ? volatile Thread thread;
? ? ? ? Node nextWaiter;
? ? ? ? final boolean isShared() {
? ? ? ? ? ? return nextWaiter == SHARED;
? ? ? ? }
/**
* 返回前節(jié)點,是空的就拋異常
*/
? ? ? ? final Node predecessor() throws NullPointerException {
? ? ? ? ? ? Node p = prev;
? ? ? ? ? ? if (p == null)
? ? ? ? ? ? ? ? throw new NullPointerException();
? ? ? ? ? ? else
? ? ? ? ? ? ? ? return p;
? ? ? ? }
? ? ? ? Node() {? ? // Used to establish initial head or SHARED marker
? ? ? ? }
? ? ? ? Node(Thread thread, Node mode) {? ? ?// Used by addWaiter
? ? ? ? ? ? this.nextWaiter = mode;
? ? ? ? ? ? this.thread = thread;
? ? ? ? }
? ? ? ? Node(Thread thread, int waitStatus) { // Used by Condition
? ? ? ? ? ? this.waitStatus = waitStatus;
? ? ? ? ? ? this.thread = thread;
? ? ? ? }
? ? }

文章到這里就告一段落了,是不是還有意猶未盡的小伙伴,想要了解更多的小伙伴可以點擊:尚學(xué)堂java基礎(chǔ)入門教_java基礎(chǔ)視頻java基礎(chǔ)教程