多線程基礎學習

多線程的好處

一、並行編程可使程序執行速度極大的提升,java自己是一種多線程語言。java

二、使用多線程能夠利用機器額外的處理器,資源充分利用。編程

簡單介紹

java的線程機制是搶佔式的,這表示調度機制會週期性的中斷線程,將上下文切換到另外一個線程,從而爲每一個線程都提供時間片,使得每一個線程都會分配到數量合緩存

理的時間去驅動它的任務,併發編程使咱們能夠將程序劃分爲多個分離的、獨立運行的任務。多線程

任務實現

經過實現Runnable接口實現線程:

public class LiftOff implements Runnable{

//打印100之內的數
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}

當從Runnable導出一個類時,必須具備run()方法,可是這個方法並沒有特殊之處————它不會產生任何內在的縣城能力,要實現線程行爲,你必須顯式地將一個任務附着到線程上。
將Runnable類提交給Thread的構造器,經過Thrad.start()啓動線程,調用以後主線程當即返回執行下面的打印邏輯,啓動的新線程再執行它的run()方法。併發

public class TestThread {
public static void main(String[] args) {
Thread t = new Thread(new LiftOff());
t.start();
System.out.println("waiting for thread !");
}

}

經過集成Thread類來實現線程:

public class SimpleThread extends Thread {
private int countDown = 5;
private static int threadCount = 0;

public SimpleThread() {
super(Integer.toString(++threadCount)); //經過構造器爲線程賦名,更名字能夠經過Thread.getName()得到
start();
}

@Override
public String toString() {
return "#" + getName() + "(" + countDown + ") .";
}

@Override
public void run() {
while (true){
System.out.println(this);
if(--countDown == 0) return;
}
}

public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new SimpleThread();
}
}
}






經過實現Callable接口實現線程:

若是但願任務在完成時可以返回一個值,那麼能夠實現Callable接口而不是Runnable接口。

public class TaskWithResult implements Callable<String> {

private int id;

public TaskWithResult(int id) {
this.id = id;
}

@Override
public String call() throws Exception {
return "result of TaskWithResult is " + id ;
}
}
測試主類:
public class TestCallThread {

public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
ArrayList<Future<String>> results = new ArrayList<>();

for (int i = 0; i < 10; i++) {
results.add(executorService.submit(new TaskWithResult(i)));
}

for (Future<String> fs : results) {
try {
System.out.println(fs.get());
} catch (InterruptedException e) {
System.out.println(e);
return;
} catch (ExecutionException e) {
e.printStackTrace();
return;
}finally {
executorService.shutdown();
}
}
}
}

ExecutorService.add()方法添加一個任務以後,會返回一個Future,經過Future的Future.isDone()能夠查詢任務是否已經完成,若是完成了能夠調用Future.get(),此時get()方法會阻塞,直至結果準備就緒。

線程池的使用

爲何使用線程池:

一、每次new Thread() 新建對象,性能差;ide

二、線程缺少統一的管理,可能無限制的新建線程,相互競爭,有可能佔用過多系統資源致使死機或OOM(內存溢出);性能

三、缺乏更多的功能,如更多執行,按期執行,線程中斷。測試

線程池的好處:

一、重用 存在的線程,減小對象的建立、消亡的開銷,性能佳;this

二、可有效的控制最大併發線程數,提升系統資源利用率,同時能夠避免過多資源競爭,避免阻塞;操作系統

三、提供定時執行,按期執行,單線程,併發控制等高級功能。

在實際多線程開發中,使用線程池管理線程是優選方法。

線程池的類:

ThreadPoolExecutor

三個重要參數:

corePoolSize:核心線程數量; 線程數少於該值,建立新線程,即便存在空閒線程。

maximumPoolSize:線程最大線程數量;線程數少於該值,且大於corePoolSize,只有當阻塞隊列滿的時候會建立線程。

workQueue:阻塞隊列,存儲等待執行的任務,很重要,會對線程池運行過程產生重大影響。當線程達到最大線程數量時,新提交的任務會添加到阻塞隊列裏,當阻塞隊列滿的時候,會使用下文的拒絕策略。

keepAliveTime:線程沒有任務執行時最多保持多久時間終止。當線程數量大於核心線程數的時候,若是一個線程超過這個時間,沒有任務提交,線程即銷燬。

unit:keepAliveTime的時間單位。

ThreadFactory:線程工廠,用來建立線程,默認會使用默認的線程工廠來建立線程,使用默認的線程工廠建立線程的時候,線程具備相同的優先級,非守護的線程,具備線程名稱的線程。

rejecthandler:當拒絕處理任務時的策略。

a、直接拋出異常(默認策略)(AbortPolicy)

b、用調用者的線程處理任務  (CallerRunsPolicy)

c、丟棄隊列中最靠前的任務。(DiscardOldestPolicy)

d、直接丟棄任務。(DiscardPolicy)

若是想使cpu有一個合理的利用:建議設置較大的阻塞隊列大小,較小的線程池容量。


public class ExecutorDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new LiftOff());
executorService.shutdown();  //使線程池繼續執行已經提交的任務,可是再也不接受新任務
}
}

幾種不一樣的Executor:

  • FixedThreadPool:一次性預先的執行代價高昂的線程分配,於是也就限制了線程的數量了。
    • 超出的數量會在隊列中等待
  • CachedThraedPool: 這種執行器在執行過程當中一般會建立於所需數量相同的線程,而後在他回收舊線程時中止建立新線程,所以他是合理的Executor的首選。只有當這種方式會引起問題時,你才須要切換到FixedThreadPool;
    • Executors.newCachedThreadPool()–>可緩存的線程池
  • SingleThreadPool:線程數量爲1的FixedThradPool,這種線程池,若是提交了多個任務,每一個任務都會在下一個任務開始以前運行結束,全部的任務將使用相同的線程每一個任務都是按照他們被提交的順序,而且是在下一個任務開始以前完成的。SingleThreadExecutor會序列化全部提交給他的任務,並會維護它本身的(隱藏)的懸掛任務隊列。
  • ScheduledThreadPool:也是定長的線程池,支持定時,週期性的任務執行

用法:

//延遲3秒執行
public static void main(String[] args) {
//定義一個實例
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println("hello!");
}
},3,TimeUnit.SECONDS);
}

//延遲1秒,每隔3秒執行一次

public static void main(String[] args) {
//定義一個實例
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("hello!");
}
},1,3,TimeUnit.SECONDS);
}




線程池的幾種狀態:

image

一、Running:接受新提交的任務,而且也能處理阻塞隊列當中的任務;

二、ShutDown:不能再接受新提交的任務,但能提交阻塞隊列保存的任務;

三、Stop:再也不接受新提交的任務,也不處理阻塞隊列中的任務;

四、Tidying:全部的線程終止,線程池中的工做線程數量爲0;

五、Terminated:

線程池重要方法:

一、execute():提交任務,交給線程池執行。

二、submit():提交任務,可以返回執行結果,execute+Future。

三、shutDown():關閉線程,等待任務都執行完。

四、shutDownNow():關閉線程,不等待任務執行完,當即關閉。

如下幾個方法能夠對線程的實例進行監控:

五、getTaskCount():線程池已執行和未執行的任務總數

六、getCompleteTaskCount():已完成的任務數量

七、getPoolSize():線程池當前的線程數量

八、getActiveCount():當前線程池中正在執行任務的線程數量

線程池的合理配置:

一、CPU密集型任務:須要儘可能壓榨CPU,參考值能夠設爲N(CPU + 1);

二、IO密集型任務,參考值能夠設置爲2*N(CPU);




線程方法

休眠    Thread.sleep()

該方法使線程停止執行給定的時間。

設置優先級    Thread.setPriority()

線程的優先級將該線程的重要性傳遞給了調度器。儘管cpu處理現有線程集的順序是不肯定的,可是調度器將傾向於讓優先級最高的線程先執行。

public class SimplePriorities implements Runnable {

private int countDown = 5;
private volatile double d ;
private int priority;


public SimplePriorities(int priority) {
this.priority = priority;
}

@Override
public String toString() {
return Thread.currentThread() + ": " + countDown;
}

@Override
public void run() {
Thread.currentThread().setPriority(priority);
while (true){
for (int i = 0; i < 100000; i++) {
d += (Math.PI + Math.E) / (double)i;
if(i % 1000 == 0){
Thread.yield();
}
System.out.println(this);
if(--countDown == 0) return;
}
}
}
}
因爲考慮到線程級別與操做系統兼容映射的問題,建議調整優先級的時候,只使用MAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY三種級別。

讓步  Thread.yield()

該方法使建議具備相同優先級的其餘線程能夠運行,可是建議不必定被採用,繼續執行的可能仍是這個線程。

後臺線程(守護線程)

後臺線程(守護線程)是指在程序運行的時候在後臺提供一種通用的服務的線程,而且這種線程並不屬於程序中不可或缺的部分。

特色:非守護線程所有結束時,程序終止,同時會殺死進程中全部的後臺線程。

必須在啓動線程以前調用 Thread.setDaemon()方法,才能把它設置爲守護線程。

isDaemon()能夠用來判斷一個線程是不是守護線程,若是是一個守護線程,那麼他建立的全部線程都是守護線程。

當非守護線程結束以後,會當即關閉正在執行的守護線程,從而不會按照既定的邏輯再次往下走,好比try…catch(){}finally{}  fianlly中的語句也不會再終止時執行。

相關文章
相關標籤/搜索