01 進(jìn)程與線程的區(qū)別
進(jìn)程:是正在執(zhí)行的一段程序,一旦程序被載入內(nèi)存準(zhǔn)備執(zhí)行泡一,那就是一個(gè)進(jìn)程颤殴。進(jìn)程是表示資源分配的基本概念,又是調(diào)度運(yùn)算的基本單位鼻忠,是系統(tǒng)的并發(fā)執(zhí)行單元涵但。
線程:?jiǎn)蝹€(gè)進(jìn)程中執(zhí)行的每個(gè)任務(wù)都是一個(gè)線程,線程是進(jìn)程中執(zhí)行運(yùn)算的最小單位帖蔓。
線程的意義:提高程序效率矮瘟,充分發(fā)揮多和計(jì)算機(jī)的優(yōu)勢(shì)。
02 多線程創(chuàng)建
1塑娇,繼承Thread類澈侠,實(shí)現(xiàn)run();
public static class MyThread extends Thread{
@Override
public void run() {
System.out.println("我的一個(gè)線程");
}
}
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
}
2,實(shí)現(xiàn)Runnable接口埋酬,將對(duì)象做為Thread的構(gòu)造參數(shù)傳入
public static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("我的一個(gè)線程");
}
}
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
}
還有比較靈活的寫法:
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名內(nèi)部類寫法");
}
});
//JDK1.8版本以后
Thread t2 = new Thread(()->{
System.out.println("蘭姆達(dá)表達(dá)式");
});
t1.start();
t2.start();
3哨啃,實(shí)現(xiàn)Callable接口的call(),允許線程執(zhí)行完以后獲取返回結(jié)果
public static class MyCall implements Callable<String> {
@Override
public String call() throws Exception {
return "返回結(jié)果";
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> task = new FutureTask<String>(new MyCall());
Thread thread = new Thread(task);
thread.start();
System.out.println(task.get());
}
03 用戶線程與守護(hù)線程
當(dāng)JVM運(yùn)行起來(lái)以后奇瘦,會(huì)有兩種線程:用戶線程與守護(hù)線程。
JVM關(guān)閉的條件:當(dāng)用戶線程運(yùn)行完畢后劲弦,即使存在守護(hù)線程耳标,它也會(huì)關(guān)閉。所以不能絕對(duì)的說(shuō):用戶線程和守護(hù)線程都運(yùn)行完畢了JVM才關(guān)閉邑跪。
在Java里有個(gè)很出名的守護(hù)線程就是:GC垃圾回收線程次坡。
做一個(gè)小測(cè)試:
public static void main(String[] args) throws ExecutionException, InterruptedException {
Thread thread1 = new Thread(()->{
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程1:"+i);
}
});
//守護(hù)線程
thread1.setDaemon(true);
thread1.start();
//用戶線程
Thread thread2 = new Thread(()->{
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程2:"+i);
}
});
thread2.start();
}
輸出結(jié)果:
線程2:94
線程2:95
線程1:25
線程2:96
線程2:97
線程2:98
線程1:26
線程2:99
Process finished with exit code 0
上面的thread1.setDaemon(true);設(shè)置成了守護(hù)線程呼猪,當(dāng)用戶線程結(jié)束后,守護(hù)線程也停止運(yùn)行了砸琅。
04 線程的優(yōu)先級(jí)
CPU內(nèi)核同一時(shí)刻只能執(zhí)行一條線程宋距,內(nèi)核靠線程調(diào)度器來(lái)分配時(shí)間片來(lái)執(zhí)行線程。所以線程需要搶奪時(shí)間片來(lái)執(zhí)行症脂,那么優(yōu)先級(jí)其實(shí)就是設(shè)置搶奪的概率谚赎。
public static void main(String[] args){
Thread thread1 = new Thread(()->{
for (int i = 0; i < 100; i++) {
System.out.println("線程1:"+i);
}
});
Thread thread2 = new Thread(()->{
for (int i = 0; i < 100; i++) {
System.out.println("線程2:"+i);
}
});
thread1.setPriority(10);
thread2.setPriority(5);
thread1.start();
thread2.start();
}
輸出結(jié)果:
線程1:97
線程1:98
線程1:99
線程2:0
線程2:1
線程2:2
線程2:3
線程2:4
可以看到線程1執(zhí)行完畢后線程2才開始執(zhí)行,說(shuō)明線程1爭(zhēng)奪時(shí)間片的概率大诱篷。
05 線程的生命周期
06 join方法
在線程執(zhí)行的時(shí)候讓別的線程調(diào)用join先插隊(duì)壶唤,等別的線程執(zhí)行完后再執(zhí)行本線程。
public static void main(String[] args){
Thread t1 = new Thread(()->{
for (int i = 0; i < 3; i++) {
System.out.println("t1:"+i);
}
});
Thread t2 = new Thread(()->{
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 3; i++) {
System.out.println("t2:"+i);
}
});
Thread t3 = new Thread(()->{
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 3; i++) {
System.out.println("t3:"+i);
}
});
t1.start();
t2.start();
t3.start();
}
輸出結(jié)果:
t1:0
t1:1
t1:2
t2:0
t2:1
t2:2
t3:0
t3:1
t3:2
Process finished with exit code 0
07 JMM內(nèi)存模型
JMM(Java Memory Model)
線程是不能夠直接修改主存內(nèi)的數(shù)據(jù)的棕所,而是先從主存中讀取到自己的工作內(nèi)存中創(chuàng)建副本闸盔,修改完成后寫入到主內(nèi)存,這就是JMM模型琳省。
這也是為什么多線程并發(fā)訪問(wèn)修改數(shù)據(jù)的時(shí)候?yàn)槭裁闯霈F(xiàn)安全問(wèn)題迎吵。
08 并發(fā)編程的三大特性
原子性
一個(gè)操作或多個(gè)操作,要么全部執(zhí)行并且執(zhí)行過(guò)程不被打斷针贬,要么全部不執(zhí)行(提供互斥訪問(wèn)击费,在同一時(shí)刻只有一個(gè)線程進(jìn)行訪問(wèn))
可以通過(guò)加鎖的方式。
先看不加鎖:
static int num = 100;
public static void main(String[] args){
Runnable runnable = ()->{
while (true){
if(num>0){
num--;
}else{
break;
}
System.out.println(num);
}
};
new Thread(runnable).start();
new Thread(runnable).start();
new Thread(runnable).start();
}
輸出了詭異的結(jié)果:
1
0
98
94
Process finished with exit code 0
如果加鎖:
static int num = 100;
public static void main(String[] args){
Object o = new Object();
Runnable runnable = ()->{
while (true){
synchronized (o){
if(num>0){
num--;
}else{
break;
}
System.out.println(num);
}
}
};
new Thread(runnable).start();
new Thread(runnable).start();
new Thread(runnable).start();
}
輸出結(jié)果:
5
4
3
2
1
0
Process finished with exit code 0
加鎖需要傳入一個(gè)對(duì)象坚踩,本程序中該對(duì)象是唯一的荡灾,如果直接寫new Object()和不加鎖效果一樣。
可見性
public static boolean flag = false;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
System.out.println("線程一號(hào)啟動(dòng)");
while (!flag){};
System.out.println("線程一號(hào)結(jié)束");
}).start();
Thread.sleep(1000);
new Thread(()->{
flag = true;
System.out.println("把flag修改為了true");
}).start();
}
顯然對(duì)于一號(hào)線程而言瞬铸,當(dāng)二號(hào)線程修改了flag的值后批幌,一號(hào)線程并沒(méi)有及時(shí)獲得flag,也就是說(shuō)flag對(duì)于一號(hào)線程是不可見的嗓节。flag的值沒(méi)有得到及時(shí)的更新荧缘。
有序性
JVM為了提高效率,會(huì)做出一些優(yōu)化拦宣,對(duì)指令進(jìn)行重排序(happens-before)截粗,在單線程的情況下沒(méi)有問(wèn)題,但是多線程編程需要考慮有序性問(wèn)題鸵隧。
volatile
1绸罗,保證可見性
2,屏蔽指令重排序
3豆瘫,但是保證不了原子性
synchronized
JDK1.0開始提供的關(guān)鍵字珊蟀,重量級(jí)的鎖,能夠保證某個(gè)代碼塊在被執(zhí)行時(shí)外驱,其他線程不訪問(wèn)執(zhí)行育灸。
synchronized必須使用對(duì)象做為鎖腻窒,因?yàn)閷?duì)象分為三部分:頭部分里面有個(gè)鎖的字段,synchronized就是利用該字段達(dá)到上鎖的目的磅崭。
注意:假如有兩個(gè)方法儿子,要想實(shí)現(xiàn)調(diào)用fun1時(shí)fun2不能被訪問(wèn),必須使用同一個(gè)對(duì)象加鎖
public static Object o ;
public static void fun1(){
synchronized (o){}
}
public static void fun2(){
synchronized (o){}
}
Monitor
JVM 是通過(guò)進(jìn)入砸喻、退出對(duì)象監(jiān)視器(Monitor)來(lái)實(shí)現(xiàn)對(duì)方法柔逼,同步塊的同步。
使用synchronized加鎖定的代碼塊恩够,在被編譯后會(huì)形成對(duì)象監(jiān)視器的入口(monitorenter)和出口(monitorexit)
使用對(duì)象Object做為鎖的時(shí)候卒落,當(dāng)有線程訪問(wèn)同步代碼塊的時(shí)候,監(jiān)視器入口(monitorenter)會(huì)去檢查Object是否上鎖蜂桶,沒(méi)有上鎖儡毕,則讓該線程訪問(wèn),同時(shí)給該對(duì)象上鎖扑媚,此時(shí)若有其他線程訪問(wèn)該代碼塊的時(shí)候腰湾,監(jiān)視器入口通過(guò)對(duì)比Object,發(fā)現(xiàn)上鎖了疆股,就會(huì)讓其他線程處于阻塞狀態(tài)费坊,當(dāng)?shù)跅l線程執(zhí)行完畢退出(monitorexir)以后,會(huì)給該對(duì)象解鎖旬痹,那么被阻塞的線程就可以接著訪問(wèn)附井,并保證了原子性。
public class Demo {
public static synchronized void get(){
//是把Demo.class(當(dāng)前類的字節(jié)碼文件)做為對(duì)象加鎖
}
public synchronized void to(){
//是把this(當(dāng)前this鎖)做為對(duì)象枷鎖
}
public static void main(String[] args) {
Demo demo = new Demo();
demo.to();
Demo.get();
}
}
對(duì)象如同鎖两残,持有鎖的線程可以在同步中執(zhí)行永毅,沒(méi)有持有鎖的線程,即使獲取CPU的執(zhí)行權(quán)人弓,也進(jìn)不去沼死。
優(yōu)點(diǎn):解決了線程安全問(wèn)題
缺點(diǎn):多個(gè)線程需要判斷鎖,較為消耗資源崔赌,搶鎖的資源
09 J.U.C之Lock
Lock
public static void main(String[] args) {
Lock lock = new ReentrantLock();
//Lock是代碼實(shí)現(xiàn)的接口意蛀,而synchronized是JVM底層實(shí)現(xiàn)的
new Thread(()->{
lock.lock();
try{
//加鎖代碼塊
}finally {
//必須手動(dòng)釋放
lock.unlock();
}
}).start();
}
trylock
上鎖部分不被訪問(wèn)的時(shí)候,去做別的事情健芭,而不是像synchronized那樣必須阻塞县钥。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo {
Lock lock = new ReentrantLock();
public void insert(Thread thread){
if(lock.tryLock()){
//搶到鎖了,就調(diào)用這里的方法
try{
System.out.println(thread.getName()+"搶到鎖啦");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println(thread.getName()+"釋放鎖啦");
}
}else{
//如果沒(méi)有搶到鎖慈迈,就調(diào)用這里的方法
System.out.println(Thread.currentThread()+"沒(méi)有搶到鎖啦");
}
}
public static void main(String[] args) {
Demo d = new Demo();
new Thread(()->{
d.insert(Thread.currentThread());
}).start();
new Thread(()->{
d.insert(Thread.currentThread());
}).start();
}
}
Lock與synchronized的區(qū)別
1)Lock是一個(gè)接口若贮,而synchronized是一個(gè)關(guān)鍵字,是Java內(nèi)置的實(shí)現(xiàn),它可以直接修飾方法和代碼塊兜看,而lock只能修飾代碼塊。
2)synchronized在發(fā)生異常的時(shí)候狭瞎,會(huì)自動(dòng)釋放線程占有的鎖细移,因此不會(huì)導(dǎo)致死鎖現(xiàn)象,而Lock發(fā)生異常以后熊锭,如果沒(méi)有unLock()釋放鎖弧轧,很可能造成死鎖現(xiàn)象,因此在使用Lock的時(shí)候需要在finally中釋放鎖碗殷。
3)Lock可以讓等待的線程響應(yīng)中斷精绎,去干別的事情,而synchronized會(huì)讓線程一直阻塞下去锌妻。
4)通過(guò)Lock的trylock()可以知道有沒(méi)有成功獲取鎖代乃,而synchronized不行。
5)Lock可以提高多線程進(jìn)行讀操作的效率(提供讀寫鎖)仿粹。
從性能上來(lái)說(shuō)搁吓,如果競(jìng)爭(zhēng)資源很激烈,Lock的性能遠(yuǎn)遠(yuǎn)大于synchronized吭历。
10 線程通信
wait與notify
使用兩個(gè)線程交替打印1-100堕仔,其中一條線程只打印奇數(shù),另外一個(gè)線程只打印偶數(shù)晌区。
public class Wait {
static class Num{
public int num = 1;//共享資源
}
static class J implements Runnable{
public Num numObj;
public J(Num n){
this.numObj = n;
}
@Override
public void run() {
synchronized (numObj){
while (numObj.num<=100){
if(numObj.num%2!=0){
System.out.println("奇數(shù)====>"+numObj.num);
numObj.num++;
}else {
try {
numObj.notify();
numObj.wait();//wait()要寫在代碼塊里面摩骨,因?yàn)樗淖饔檬轻尫沛i,并且等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
static class O implements Runnable{
public Num numObj;
public O(Num n){
this.numObj = n;
}
@Override
public void run() {
synchronized (numObj){
while (numObj.num<=100) {
if (numObj.num % 2 == 0) {
System.out.println("偶數(shù)====>" + numObj.num);
numObj.num++;
} else {
try {
numObj.notify();
numObj.wait();//wait()要寫在代碼塊里面朗若,因?yàn)樗淖饔檬轻尫沛i恼五,并且等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public static void main(String[] args) {
Num numObj = new Num();
new Thread(new J(numObj)).start();//奇數(shù)線程
new Thread(new O(numObj)).start();//偶數(shù)線程
}
}
wait()的作用是,釋放當(dāng)前鎖捡偏,并使當(dāng)前線程處于等待狀態(tài)唤冈,所以需要寫在同步代碼塊里,notify()是喚醒一個(gè)處于等待狀態(tài)的線程银伟,本題只有兩條線程你虹,如果有多條線程,那么可以使用notifyAll()彤避。
這三個(gè)方法最終調(diào)用的都是JVM級(jí)的native方法傅物。
11 什么是線程池
線程池是Java的并發(fā)框架,幾乎所有的并發(fā)執(zhí)行程序或者異步操作都可以使用線程池琉预。
1)降低資源消耗董饰。通過(guò)重復(fù)使用已經(jīng)創(chuàng)建好的線程降低創(chuàng)建和銷毀的消耗。
2)提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí)卒暂,不需要等到線程創(chuàng)建后就可以立即執(zhí)行啄栓。
3)提高現(xiàn)成的可管理性。使用線程池可以進(jìn)行統(tǒng)一的分配也祠、調(diào)優(yōu)昙楚、監(jiān)控。
12 線程池工作原理
Executor是最基本的接口诈嘿,其子接口:ExecutorService是工作中常用的接口
其中一個(gè)很重要的實(shí)現(xiàn)類是ThreadPoolExecutor堪旧,通過(guò)它new出來(lái)
import java.util.concurrent.*;
public class Pool {
static class MyRunnable implements Runnable{
String info = "";
public MyRunnable(String txt){
this.info = txt;
}
@Override
public void run() {
System.out.println(this.info);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// int corePoolSize 核心線程數(shù)
// int maximumPoolSize 最大線程數(shù)
// long keepAliveTime 保持存活得時(shí)間
// TimeUnit unit 時(shí)間單位
// BlockingQueue<Runnable> workQueue 任務(wù)隊(duì)列
// RejectedExecutionHandler handler 飽和策略
ExecutorService es = new ThreadPoolExecutor(
2,//corePoolSize
5,//maximumPoolSize
10,//keepAliveTime
TimeUnit.SECONDS,//unit
new ArrayBlockingQueue<Runnable>(5),
new ThreadPoolExecutor.AbortPolicy());
for (int i = 1; i <= 20; i++) {
try{
es.execute(new MyRunnable("執(zhí)行第"+i+"條線程"));
}catch (Throwable e){
System.out.println("丟棄線程"+i);
}
}
es.shutdown();
}
}
四個(gè)流程:
1)判斷核心線程數(shù)
2)判斷任務(wù)隊(duì)列
3)判斷最大線程數(shù)(備胎線程)
4)執(zhí)行飽和策略
存活時(shí)間參數(shù):當(dāng)最大線程數(shù)(備胎線程)執(zhí)行完畢,并且沒(méi)有新任務(wù)的前提下奖亚,只能存活(keepAliveTime unit )的時(shí)間淳梦,超過(guò)時(shí)間就會(huì)被釋放掉。
13 三種常見隊(duì)列
SynchronousQueue
一次性只能裝一個(gè)任務(wù)昔字,其他任務(wù)處于阻塞狀態(tài)爆袍,同時(shí)一次性只能取出一個(gè)任務(wù),如果沒(méi)有任務(wù)可以取出作郭,也處于阻塞狀態(tài)
import java.util.concurrent.SynchronousQueue;
public class Queue {
public static void main(String[] args) {
SynchronousQueue<Integer> queue = new SynchronousQueue<>();
new Thread(()->{
for (int i = 0; i < 20; i++) {
try {
queue.put(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("裝入數(shù)據(jù)====>"+i);
}
}).start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
System.out.println("取出數(shù)據(jù)=====>"+queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
//執(zhí)行結(jié)果
取出數(shù)據(jù)=====>0
裝入數(shù)據(jù)====>0
隔了兩秒
取出數(shù)據(jù)=====>1
裝入數(shù)據(jù)====>1
隔了兩秒
取出數(shù)據(jù)=====>2
裝入數(shù)據(jù)====>2‘
隔了兩秒
取出數(shù)據(jù)=====>3
裝入數(shù)據(jù)====>3
LinkedBlockingQueue
瞬間裝完所有的任務(wù)螃宙,然后慢慢取出
import java.util.concurrent.LinkedBlockingQueue;
public class Queue {
public static void main(String[] args) {
LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
new Thread(()->{
for (int i = 0; i < 20; i++) {
try {
queue.put(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("裝入數(shù)據(jù)====>"+i);
}
}).start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
System.out.println("取出數(shù)據(jù)=====>"+queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
//執(zhí)行結(jié)果
裝入數(shù)據(jù)====>18
裝入數(shù)據(jù)====>19
瞬間裝完20個(gè)數(shù)據(jù)
取出數(shù)據(jù)=====>0
隔了兩秒
取出數(shù)據(jù)=====>1
隔了兩秒
取出數(shù)據(jù)=====>2
隔了兩秒
取出數(shù)據(jù)=====>3
ArrayBlockingQueue
設(shè)置填裝大小,比如4所坯,那么一次性裝入4個(gè)谆扎,其余的取出一個(gè)就裝一個(gè)
import java.util.concurrent.ArrayBlockingQueue;
public class Queue {
public static void main(String[] args) {
ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(4);
new Thread(()->{
for (int i = 0; i < 20; i++) {
try {
queue.put(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("裝入數(shù)據(jù)====>"+i);
}
}).start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
System.out.println("取出數(shù)據(jù)=====>"+queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
//執(zhí)行結(jié)果
裝入數(shù)據(jù)====>0
裝入數(shù)據(jù)====>1
裝入數(shù)據(jù)====>2
裝入數(shù)據(jù)====>3
瞬間完成
取出數(shù)據(jù)=====>0
裝入數(shù)據(jù)====>4
隔了兩秒
取出數(shù)據(jù)=====>1
裝入數(shù)據(jù)====>5
隔了兩秒
14 飽和策略
CallerRunsPolicy
不拋棄任務(wù),調(diào)用線程池的線程芹助,幫助執(zhí)行任務(wù)
比如上面的main方法調(diào)用的線程池堂湖,16號(hào)線程就會(huì)在main中執(zhí)行
演示代碼:
import java.util.concurrent.*;
public class Pool {
static class MyRunnable implements Runnable{
String info = "";
public MyRunnable(String txt){
this.info = txt;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+this.info);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ExecutorService es = new ThreadPoolExecutor(
2,
5,
10,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(10),
new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 1; i <= 20; i++) {
try{
es.execute(new MyRunnable("執(zhí)行第"+i+"條線程"));
}catch (Throwable e){
System.out.println("丟棄線程"+i);
}
}
es.shutdown();
}
}
執(zhí)行結(jié)果:
pool-1-thread-2:執(zhí)行第2條線程
pool-1-thread-1:執(zhí)行第1條線程
main:執(zhí)行第16條線程
pool-1-thread-3:執(zhí)行第13條線程
pool-1-thread-4:執(zhí)行第14條線程
pool-1-thread-5:執(zhí)行第15條線程
AbortPolicy(默認(rèn))
當(dāng)最大線程數(shù)滿了以后,拋出異常状土,拋棄任務(wù)
DiscardPolicy
連異常都不拋无蜂,直接把任務(wù)丟了
DiscardOldestPolicy
連異常都不拋,直接把任務(wù)丟了
15 線程池工具類
工具類:Executors 快速創(chuàng)建線程池
緩存線程池
newCachedThreadPool
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Es {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
System.out.println("線程"+i);
executorService.execute(()->{
for (int j = 0; j < 5; j++) {
System.out.println(Thread.currentThread().getName()+":"+j);
}
});
}
executorService.shutdown();
}
}
源碼的實(shí)現(xiàn):
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
可以發(fā)現(xiàn):沒(méi)有核心線程數(shù)蒙谓,扔進(jìn)去多少線程斥季,就會(huì)創(chuàng)建多少線程,好處是線程復(fù)用以及可以及時(shí)釋放線程累驮,弊端就是酣倾,當(dāng)任務(wù)量極大的時(shí)候,他就創(chuàng)建一大堆線程谤专。
定長(zhǎng)線程池
newFixedThreadPool
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Es {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++) {
System.out.println("線程"+i);
executorService.execute(()->{
for (int j = 0; j < 5; j++) {
System.out.println(Thread.currentThread().getName()+":"+j);
}
});
}
executorService.shutdown();
}
}
源碼的實(shí)現(xiàn):
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
單例線程池
newSingleThreadExecutor
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Es {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
System.out.println("線程"+i);
executorService.execute(()->{
for (int j = 0; j < 5; j++) {
System.out.println(Thread.currentThread().getName()+":"+j);
}
});
}
executorService.shutdown();
}
}
可以發(fā)現(xiàn)單例線程池躁锡,會(huì)由一個(gè)線程完成所有的任務(wù)。
源碼的實(shí)現(xiàn):
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
調(diào)度線程池
ScheduledExecutorService:newScheduledThreadPool
可以實(shí)現(xiàn):延遲執(zhí)行的線程池
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
//延遲執(zhí)行的線程池
scheduledExecutorService.schedule(()->{
System.out.println("過(guò)去5秒種啦");
},5, TimeUnit.SECONDS);//第二個(gè)參數(shù)是延遲時(shí)間置侍,然后是時(shí)間單位
scheduledExecutorService.shutdown();
}
可以實(shí)現(xiàn):周期執(zhí)行的線程池
public static void main(String[] args) {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
//周期執(zhí)行的線程池:5s之后開始執(zhí)行任務(wù)映之,每隔2s重復(fù)執(zhí)行一次
scheduledExecutorService.scheduleAtFixedRate(()->{
System.out.println("執(zhí)行啦");
},5,2, TimeUnit.SECONDS);//參數(shù):延遲時(shí)間 間隔時(shí)間 時(shí)間單位
//scheduledExecutorService.shutdown();這時(shí)候不要shutdown
}