線程的基本介紹
1.什么是進(jìn)程
進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序
每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專用且受保護(hù)的內(nèi)存空間內(nèi)

2.什么是線程
1個(gè)進(jìn)程要想執(zhí)行任務(wù),必須得有線程(每1個(gè)進(jìn)程至少要有1條線程)
線程是進(jìn)程的基本執(zhí)行單元,一個(gè)進(jìn)程(程序)的所有任務(wù)都在線程中執(zhí)行
比如使用酷狗播放音樂、使用迅雷下載電影,都需要在線程中執(zhí)行

3.線程的串行
1個(gè)線程中任務(wù)的執(zhí)行是串行的
如果要在1個(gè)線程中執(zhí)行多個(gè)任務(wù),那么只能一個(gè)一個(gè)地按順序執(zhí)行這些任務(wù)
也就是說,在同一時(shí)間內(nèi),1個(gè)線程只能執(zhí)行1個(gè)任務(wù)
比如在1個(gè)線程中下載3個(gè)文件(分別是文件A、文件B、文件C)

4.線程的生命周期

1.新建
當(dāng)程序使用 new 關(guān)鍵字創(chuàng)建了一個(gè)線程之后,該線程就處于新建狀態(tài),
此時(shí)僅由 JVM 為其分配 內(nèi)存,并初始化其成員變量的值.
2.就緒
當(dāng)線程對(duì)象調(diào)用了 start()方法之后,該線程處于就緒狀態(tài)。
Java 虛擬機(jī)會(huì)為其創(chuàng)建方法調(diào)用棧和程序計(jì)數(shù)器,等待調(diào)度運(yùn)行。
3.運(yùn)行
如果處于就緒狀態(tài)的線程被調(diào)度獲得CPU執(zhí)行權(quán),就會(huì)執(zhí)行run()方法的邏輯,此時(shí)處于運(yùn)行狀態(tài)。
4.阻塞
阻塞狀態(tài)是指線程因?yàn)槟撤N原因放棄CPU使用權(quán),暫時(shí)停止運(yùn)行.
需要等到線程進(jìn)入就緒狀態(tài)才有機(jī)會(huì)獲得cpu時(shí)間片從而執(zhí)行.
這個(gè)狀態(tài)分下面三種情況
1.等待阻塞(obj.wait()->進(jìn)入wait):即運(yùn)行中的線程執(zhí)行wait方法,JVM會(huì)將該線程放入等待隊(duì)列中
2.同步阻塞(lock.lock()/synchronized->鎖池):即運(yùn)行中的線程獲取對(duì)象的
同步鎖(指jvm提供的內(nèi)置鎖synchronized)或者顯示鎖lock失敗,會(huì)將線程阻塞掛起
3.其他方式阻塞(sleep/join):運(yùn)行中的線程執(zhí)行Thread.sleep()或者thread.join()方法,
或者發(fā)出I/O請求待處理的時(shí)候,jvm會(huì)將線程置為阻塞狀態(tài)。
當(dāng)sleep()狀態(tài)超時(shí)、join()等待線程運(yùn)行結(jié)束或者超時(shí)、或者處理I/O完畢,會(huì)重新進(jìn)入就緒狀態(tài)
5.死亡
線程結(jié)束任務(wù)之后自己結(jié)束,或者產(chǎn)生了異常而結(jié)束。
5.創(chuàng)建線程
線程的創(chuàng)建一共有四種方式:
1.繼承于Thread類,重寫run()方法。
2.實(shí)現(xiàn)Runable接口,實(shí)現(xiàn)里面的run()方法。
3.使用 Future Task 實(shí)現(xiàn)有返回結(jié)果的線程。
4.使用線程池。
//繼承于Thread類,重寫run()方法
class MyThread extends Thread{
//重寫run方法
@Override
public void run() {
//任務(wù)內(nèi)容....
System.out.println("當(dāng)前線程是:"+Thread.currentThread().getName());
}
}
Thread thread = new MyThread();
//線程啟動(dòng)
thread.start();
//當(dāng)前線程是:Thread-0
使用匿名內(nèi)部類
Thread thread = new Thread(){
@Override
public void run() {
System.out.println("當(dāng)前線程是:"+Thread.currentThread().getName());
}
};
因?yàn)閖ava是單繼承結(jié)構(gòu),一旦繼承了Thread類,就無法繼承其他類了。所以建議使用 實(shí)現(xiàn)Runable接口 的方法
//實(shí)現(xiàn)Runable接口,實(shí)現(xiàn)里面的run()方法:
class MyTask implements Runnable{
//重寫run方法
public void run() {
//任務(wù)內(nèi)容....
System.out.println("當(dāng)前線程是:"+Thread.currentThread().getName());
}
}
Thread thread = new Thread(new MyTask());
//線程啟動(dòng)
thread.start();
使用匿名內(nèi)部類
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("當(dāng)前線程是:"+Thread.currentThread().getName());
}
});
FutureTask是一個(gè)可取消的異步計(jì)算任務(wù),是一個(gè)獨(dú)立的類,實(shí)現(xiàn)了 Future、Runnable接口。FutureTask的出現(xiàn)是為了彌補(bǔ) Thread的不足而設(shè)計(jì)的,可以讓程序員跟蹤、獲取任務(wù)的執(zhí)行情況、計(jì)算結(jié)果 。
因?yàn)?code>FutureTask實(shí)現(xiàn)了 Runnable,所以FutureTask可以作為參數(shù)來創(chuàng)建一個(gè)新的線程來執(zhí)行,也可以提交給 Executor 執(zhí)行。FutureTask一旦計(jì)算完成,就不能再重新開始或取消計(jì)算。
FutureTask的構(gòu)造方法
可以接受 Runnable,Callable 的子類實(shí)例。
//創(chuàng)建一個(gè) FutureTask,一旦運(yùn)行就執(zhí)行給定的 Callable。
public FutureTask(Callable<V> callable);
//創(chuàng)建一個(gè) FutureTask,一旦運(yùn)行就執(zhí)行給定的 Runnable,并安排成功完成時(shí) get 返回給定的結(jié)果 。
public FutureTask(Runnable runnable, V result)
//FutureTask 的簡單例子
MyCallable.java
public class MyCallable implements Callable<Double>{
@Override
public Double call() throws Exception {
double d = 0;
try {
System.out.println("異步計(jì)算開始.......");
d = Math.random()*10;
d += 1000;
Thread.sleep(2000);
System.out.println("異步計(jì)算結(jié)束.......");
} catch (InterruptedException e) {
e.printStackTrace();
}
return d;
}
}
Test.java
public class Test {
public static void main(String[] args) {
FutureTask<Double> task = new FutureTask<>(new MyCallable());
//創(chuàng)建一個(gè)線程,異步計(jì)算結(jié)果
Thread thread = new Thread(task);
thread.start();
//主線程繼續(xù)工作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主線程等待計(jì)算結(jié)果...");
//當(dāng)需要用到異步計(jì)算的結(jié)果時(shí),阻塞獲取這個(gè)結(jié)果
Double d;
try {
d = task.get();
System.out.println("計(jì)算結(jié)果是:"+d);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//用同一個(gè) FutureTask 再起一個(gè)線程
Thread thread2 = new Thread(task);
thread2.start();
}
}

第四種先簡單給個(gè)例子,具體看下面
JDK中提供了工具類
Executors,提供了幾個(gè)創(chuàng)建常用的線程池的工廠方法
package wgzyx;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyRunable implements Runnable{
private String taskName;
public MyRunable(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
System.out.println("線程池完成任務(wù):"+taskName);
}
public static void main(String[] args) {
//創(chuàng)建一個(gè)只有一個(gè)線程的線程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
//創(chuàng)建任務(wù),并提交任務(wù)到線程池中
executorService.execute(new MyRunable("任務(wù)1"));
executorService.execute(new MyRunable("任務(wù)2"));
executorService.execute(new MyRunable("任務(wù)3"));
}
}
/*
線程池完成任務(wù):任務(wù)1
線程池完成任務(wù):任務(wù)2
線程池完成任務(wù):任務(wù)3
*/
多線程
1.什么是多線程
1個(gè)進(jìn)程中可以開啟多條線程,每條線程可以并行(同時(shí))執(zhí)行不同的任務(wù)
多線程技術(shù)可以提高程序的執(zhí)行效率
比如同時(shí)開啟3條線程分別下載3個(gè)文件(分別是文件A、文件B、文件C)

2.多線程的原理
同一時(shí)間,CPU只能處理1條線程,只有1條線程在工作(執(zhí)行)
多線程并發(fā)(同時(shí))執(zhí)行,其實(shí)是CPU快速地在多條線程之間調(diào)度(切換)
如果CPU調(diào)度線程的時(shí)間足夠快,就造成了多線程并發(fā)執(zhí)行的假象
如果線程非常非常多
CPU會(huì)在N多線程之間調(diào)度,CPU會(huì)累死,消耗大量的CPU資源
每條線程被調(diào)度執(zhí)行的頻次會(huì)降低線程的執(zhí)行效率降低
3.多線程的優(yōu)缺點(diǎn)
- 線程的優(yōu)點(diǎn)
能適當(dāng)提高程序的執(zhí)行效率
能適當(dāng)提高資源利用率(CPU、內(nèi)存利用率) - 多線程的缺點(diǎn)
開啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下,主線程占用1M,子線程占用512KB),如果開啟大量的線程,會(huì)占用大量的內(nèi)存空間,降低程序的性能
線程越多,CPU在調(diào)度線程上的開銷就越大
程序設(shè)計(jì)更加復(fù)雜:比如線程之間的通信、多線程的數(shù)據(jù)共享
4. 何時(shí)建議使用多線程
?、? 當(dāng)主線程試圖執(zhí)行冗長的操作,但系統(tǒng)會(huì)卡界面,體驗(yàn)非常不好,這時(shí)候可以開辟一個(gè)新線程,來處理這項(xiàng)冗長的工作。
?、? 當(dāng)請求別的數(shù)據(jù)庫服務(wù)器、業(yè)務(wù)服務(wù)器等,可以開辟一個(gè)新線程,讓主線程繼續(xù)干別的事。
③. 利用多線程拆分復(fù)雜運(yùn)算,提高計(jì)算速度
線程池
1.為什么使用線程池
如果并發(fā)的線程數(shù)量很多,并且每個(gè)線程都是執(zhí)行一個(gè)時(shí)間很短的任務(wù)就結(jié)束了,這樣頻繁創(chuàng)建線程就會(huì)大大降低系統(tǒng)的效率,因?yàn)轭l繁創(chuàng)建線程和銷毀線程需要時(shí)間。
2.線程池的作用
使得線程可以復(fù)用,就是執(zhí)行完一個(gè)任務(wù),并不被銷毀,而是可以繼續(xù)執(zhí)行其他的任務(wù)。
3.線程池的好處
1.降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
2.提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要的等到線程創(chuàng)建就能立即執(zhí)行。
3.提高線程的可管理性。線程是稀缺資源,如果無限制的創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。
創(chuàng)建線程池
阿里的 Java開發(fā)手冊,上面有線程池的一個(gè)建議:

主要是底層的阻塞隊(duì)列
LinkedBlockingQueue是一個(gè)用鏈表實(shí)現(xiàn)的有界阻塞隊(duì)列,容量可以選擇進(jìn)行設(shè)置,不設(shè)置的話,將是一個(gè)無邊界的阻塞隊(duì)列,最大長度為Integer.MAX_VALUE這樣的處理方式讓寫的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。
通過創(chuàng)建
ThreadPoolExecutor 對(duì)象來創(chuàng)建1.ThreadPoolExecutor參數(shù)介紹
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
1.corePoolSize:表示核心線程池的大小。當(dāng)提交一個(gè)任務(wù)時(shí),如果當(dāng)前核心線程池的線程個(gè)數(shù)沒有達(dá)到corePoolSize,則會(huì)創(chuàng)建新的線程來執(zhí)行所提交的任務(wù),即使當(dāng)前核心線程池有空閑的線程。如果當(dāng)前核心線程池的線程個(gè)數(shù)已經(jīng)達(dá)到了corePoolSize,則不再重新創(chuàng)建線程。如果調(diào)用了prestartCoreThread()或者 prestartAllCoreThreads(),線程池創(chuàng)建的時(shí)候所有的核心線程都會(huì)被創(chuàng)建并且啟動(dòng)。*
2.maximumPoolSize:表示線程池能創(chuàng)建線程的最大個(gè)數(shù)。如果當(dāng)阻塞隊(duì)列已滿時(shí),并且當(dāng)前線程池線程個(gè)數(shù)沒有超過maximumPoolSize的話,就會(huì)創(chuàng)建新的線程來執(zhí)行任務(wù)。
3.keepAliveTime:空閑線程存活時(shí)間。如果當(dāng)前線程池的線程個(gè)數(shù)已經(jīng)超過了corePoolSize,并且線程空閑時(shí)間超過了keepAliveTime的話,就會(huì)將這些空閑線程銷毀,這樣可以盡可能降低系統(tǒng)資源消耗。
4.unit:時(shí)間單位。為keepAliveTime指定時(shí)間單位。
-
workQueue:阻塞隊(duì)列。用于保存任務(wù)的阻塞隊(duì)列,可以使用ArrayBlockingQueue, LinkedBlockingQueue, SynchronousQueue, PriorityBlockingQueue。 -
threadFactory:創(chuàng)建線程的工程類。可以通過指定線程工廠為每個(gè)創(chuàng)建出來的線程設(shè)置更有意義的名字,如果出現(xiàn)并發(fā)問題,也方便查找問題原因。
7.handler:飽和策略。當(dāng)線程池的阻塞隊(duì)列已滿和指定的線程都已經(jīng)開啟,說明當(dāng)前線程池已經(jīng)處于飽和狀態(tài)了,那么就需要采用一種策略來處理這種情況。采用的策略有這幾種:
1. AbortPolicy: 直接拒絕所提交的任務(wù),并拋出RejectedExecutionException異常;
2. CallerRunsPolicy:只用調(diào)用者所在的線程來執(zhí)行任務(wù);
3. DiscardPolicy:不處理直接丟棄掉任務(wù);
4. DiscardOldestPolicy:丟棄掉阻塞隊(duì)列中存放時(shí)間最久的任務(wù),執(zhí)行當(dāng)前任務(wù).
適當(dāng)?shù)淖枞?duì)列
插入方法 add(e) offer(e) put(e) offer(e,time,unit)
移除方法 remove() poll() take() poll(time,unit)
檢查方法 element() peek()
ArrayBlockingQueue :一個(gè)由數(shù)組結(jié)構(gòu)組成的有界阻塞隊(duì)列。
LinkedBlockingQueue :一個(gè)由鏈表結(jié)構(gòu)組成的有界阻塞隊(duì)列。
PriorityBlockingQueue :一個(gè)支持優(yōu)先級(jí)排序的無界阻塞隊(duì)列。
DelayQueue: 一個(gè)使用優(yōu)先級(jí)隊(duì)列實(shí)現(xiàn)的無界阻塞隊(duì)列。
SynchronousQueue: 一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列。
LinkedTransferQueue: 一個(gè)由鏈表結(jié)構(gòu)組成的無界阻塞隊(duì)列。
LinkedBlockingDeque: 一個(gè)由鏈表結(jié)構(gòu)組成的雙向阻塞隊(duì)列。

2.ThreadPoolExecutor例子
package wgzyx;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPool {
//newFixedThreadPool創(chuàng)建固定大小的線程池。
//每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到線程達(dá)到線程池的最大大小。
// 線程池的大小一旦達(dá)到最大值就會(huì)保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束,那么線程池會(huì)補(bǔ)充一個(gè)新線程。
//線程池接口是ExecutorService
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(
nThreads,// corePoolSize
nThreads,// maximumPoolSize == corePoolSize
0L,// 空閑時(shí)間限制是 0
TimeUnit.MILLISECONDS,
//TimeUnit.DAYS天 TimeUnit.HOURS小時(shí) TimeUnit.MINUTES分鐘 TimeUnit.SECONDS秒 TimeUnit.MILLISECONDS毫秒
new LinkedBlockingQueue<Runnable>(nThreads)//一個(gè)由鏈表結(jié)構(gòu)組成的有界阻塞隊(duì)列
);
}
// newCachedThreadPool創(chuàng)建一個(gè)可緩存的線程池。
//如果線程池的大小超過了處理任務(wù)所需要的線程,那么就會(huì)回收部分空閑(60秒不執(zhí)行任務(wù))的線程,
//當(dāng)任務(wù)數(shù)增加時(shí),此線程池又可以智能的添加新線程來處理任務(wù)。
//此線程池不會(huì)對(duì)線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)(或者說JVM)能夠創(chuàng)建的最大線程大小。
public static ExecutorService newCachedThreadPool(){
return new ThreadPoolExecutor(
0, // corePoolSoze == 0
Integer.MAX_VALUE, // maximumPoolSize 非常大
60L, // 空閑判定是60 秒
TimeUnit.SECONDS,//分鐘
// 神奇的無存儲(chǔ)空間阻塞隊(duì)列,每個(gè) put 必須要等待一個(gè) take
new SynchronousQueue<Runnable>()
);
}
//newSingleThreadExecutor創(chuàng)建一個(gè)單線程的線程池。
//這個(gè)線程池只有一個(gè)線程在工作,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)。
//如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束,那么會(huì)有一個(gè)新的線程來替代它。此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行。
//實(shí)際上FinalizableDelegatedExecutorService這個(gè)類就是對(duì)ExecutorService進(jìn)行了一個(gè)包裝,防止暴露出不該被暴露的方法
//,然后加上了finalize方法保證線程池的關(guān)閉
public static ExecutorService newSingleThreadExecutor() {
return
new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1));
}
public static void main(String[] args) {
System.out.println("使用固定大小的線程池");
ExecutorService newCachedThreadPool = newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
newCachedThreadPool.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + "----" + index);
}
});
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("使用可緩存的線程池");
newCachedThreadPool = newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
newCachedThreadPool.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + "----" + index);
}
});
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("使用單線程的線程池");
newCachedThreadPool = newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
newCachedThreadPool.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + "----" + index);
}
});
}
}
}

文章參考
http://www.cnblogs.com/yxt9322yxt/p/4804026.html
https://www.cnblogs.com/jinggod/p/8485106.html