java自製線程池,java自製線程
個人部落格:www.zalezone.cn
java自製線程池
- 1.簡介
- 1.1.線程池
- 1.2.線程池作用
- 1.3.線程池優點
- 1.4.JDK內建線程池
- 1.4.1.ThreadPoolExecutor
- 2.自製線程池
- 2.1.線程池主體類
- 2.2.任務類
- 2.3.測試類別
簡介線程池
線程池的基本思想是一種對象池的思想,先開闢一塊記憶體空間,開許多等待任務的線程,一旦有任務出現就直接使用線程池中的線程來進行任務,等到任務結束後再將這個線程放入線程池中,池中的執行調度由線程池管理器來管理。
線程池作用
線程池的作用就是更好的對系統中的線程進行管理,根據系統的環境清空,可以自動或者手動的設定線程數量,達到啟動並執行最佳效果。
線程池優點
相對於不使用線程池來說,使用線程池有什麼優點呢?
JDK內建線程池
自從JDK1.5之後加入java.util.concurrent包後,線程池得到了極大的最佳化,現在只要按照提供的API來使用,我們就可以非常容易的使用JDK內建的線程池,為我們在寫自己的程式時提供了極大的方便。
ThreadPoolExecutor
先來看看ThreadPoolExecutor的完整構造方法
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
對於裡面的參數含義如下:
corePoolSize - 池中所儲存的線程數,包括空閑線程。maximumPoolSize - 池中允許的最大線程數。keepAliveTime - 當線程數大於核心時,此為終止前多餘的空閑線程等待新任務的最長時間。unit - keepAliveTime 參數的時間單位。workQueue - 執行前用於保持任務的隊列。此隊列僅保持由 execute 方法提交的 Runnable 任務。threadFactory - 執行程式建立新線程時使用的工廠。handler - 由於超出線程範圍和隊列容量而使執行被阻塞時所使用的處理常式。
下面介紹一些常用的線程池:
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}
使用方法樣本
//建立一個單線程的線程池ExecutorService pool = Executors.newSingleThreadExecutor();//建立實現了Runnable介面對象,Thread對象當然也實現了Runnable介面Thread t1 = new MyThread();//將線程放入池中進行執行pool.execute(t1);//關閉線程池pool.shutdown();
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}
使用方法樣本
//建立一個可重用固定線程數的線程池ExecutorService pool = Executors.newFixedThreadPool(2);//建立實現了Runnable介面對象,Thread對象當然也實現了Runnable介面Thread t1 = new MyThread();//將線程放入池中進行執行pool.execute(t1);//關閉線程池pool.shutdown();
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}
使用方法樣本
//建立一個可根據需要建立新線程的線程池,但是在以前構造的線程可用時將重用它們ExecutorService pool = Executors.newCachedThreadPool();//建立實現了Runnable介面對象,Thread對象當然也實現了Runnable介面Thread t1 = new MyThread();//將線程放入池中進行執行pool.execute(t1);//關閉線程池pool.shutdown();
////建立一個線程池,它可安排在給定延遲後運行命令或者定期地執行。ExecutorService pool = Executors.newScheduledThreadPool(2);//建立實現了Runnable介面對象,Thread對象當然也實現了Runnable介面Thread t1 = new MyThread();Thread t2 = new MyThread();//將線程放入池中進行執行pool.execute(t1);//使用順延強制風格的方法pool.schedule(t2, 10, TimeUnit.MILLISECONDS);//關閉線程池pool.shutdown();
自製線程池
現在讓我們來自己動手寫一個線程池。這隻是一個簡單的線程池,有助於我們更好的瞭解線程池的原理。
線程池主體類
MyThread.java
import java.util.LinkedList;/**** @author zlk* 線程池,繼承ThreadGroup,ThreadGroup用於處理一組線程的類,它是一種樹狀結構,它的下層節點還是可以是ThreadGroup對象*/public class MyThreadPool extends ThreadGroup{private boolean isAlive;//標誌線程池是否開啟private LinkedList taskQueue;//線程池中的任務隊列private int threadID;//線程池中的線程IDprivate static int threadPoolID;//線程池ID/*** 建立新的線程池,numThreads是池中的線程數* @param numThreads*/public MyThreadPool(int numThreads) {super("ThreadPool-"+(threadPoolID++));//設定該線程池是daemon屬性為true,表示當該線程池中所有的線程都被銷毀時,該線程池會被自動銷毀。super.setDaemon(true);this.isAlive = true;//建立一個任務隊列this.taskQueue = new LinkedList();//啟動numThreads個背景工作執行緒for (int i = 0; i < numThreads; i++) {new PooledThread().start();}}/*** 添加新任務* @param task*/public synchronized void performTask(Task task){if(!this.isAlive){//線程池被關則拋出IllegalStateException異常throw new IllegalStateException();}if (task != null) {//將任務放到任務隊列的尾部this.taskQueue.add(task);//通知背景工作執行緒取任務notify();}}/*** 擷取任務*/protected synchronized Task getTask()throws InterruptedException {//如果工作清單為空白,而且線程池沒有被關閉,則繼續等待任務while (this.taskQueue.size() == 0) {if (!this.isAlive) {return null;}wait();}//取工作清單中的第一個任務return (Task)this.taskQueue.removeFirst();}/*** 關閉線程池,所有線程停止,不再執行任務*/public synchronized void forceclose(){if (isAlive) {this.isAlive = false;//清除任務this.taskQueue.clear();//終止線程池中的所有線程this.interrupt();}}/*** 關閉線程池,並且等待線程池中的所有任務被運行完畢,但是不能接受新的任務*/public void shutdown(){//通知其他等待線程“該線程池已關閉“的訊息synchronized (this) {isAlive = false;notify();}//等待所有線程完成//首先建立一個新的線程數組。activeCount方法擷取線程池中活動線程的估計數Thread[] threads = new Thread[this.activeCount()];//將線程池中的活動線程拷貝到新建立的線程數組中int count = this.enumerate(threads);for (int i = 0; i < count; i++) {try {//等待線程運行結束threads[i].join();} catch (InterruptedException e) {e.printStackTrace();}}}/*** 內部類,用於執行任務的背景工作執行緒*/private class PooledThread extends Thread{//構造方法public PooledThread(){//第一個參數為該線程所在的線程組的對象,即當前線程池的對象//第二個參數為線程名字super(MyThreadPool.this,"PooledThread-"+(threadID++));}public void run(){//如果線程沒有被終止while (!isInterrupted()) {//擷取任務Task task = null;try {task = getTask();} catch (InterruptedException e) {e.printStackTrace();}//只要線程池的工作清單不為空白,getTask方法總能夠得到一個任務。//若getTask()返回null,則表示線程池中已經沒有任務了,而且線程池已被關閉。if(task == null){return;}//運行任務,吸收異常try {task.perform();} catch (Throwable t) {//當線程組中的線程有未被捕獲的異常發生時,JVM就回去調用uncaughtException方法uncaughtException(this, t);}}}}}
任務類
Task.java //作為執行任務的總介面
public interface Task {/*** 執行任務* throws Exception 執行過程中可能出現的異常*/public void perform()throws Exception;}
MyTask.java //實現了任務介面
public class MyTask implements Task{private int taskID = 0; //任務的IDpublic MyTask(int id) {this.taskID = id;}/*** 實現Task介面的perform方法*/@Overridepublic void perform() throws Exception {System.out.println("MyTask"+ taskID + ":start");//休眠一秒try {Thread.sleep(1000);} catch (Exception e) {e.printStackTrace();}System.out.println("MyTask" + taskID + ":end");}}
測試類別
PoolTest.java //測試自製線程池
import com.zale.threadpool.MyTask;import com.zale.threadpool.MyThreadPool;public class PoolTest {public static void main(String[] args){int numThreads = 3; //線程池中的線程數MyThreadPool threadPool = new MyThreadPool(numThreads);//產生線程池int numTasks = 10; //任務數//運行任務for (int i = 0; i < numTasks; i++) {threadPool.performTask(new MyTask(i));}//關閉線程池並等待所有任務完成threadPool.shutdown();}}
有對java線程池用的比較熟的不?
java中的函數其實是一大堆封裝好的代碼(那些代碼往往常常會被用到,所以封裝成一個簡單的函數供你調用),調用函數的過程就是執行那些代碼的過程,另外搭建在手機上,pda上的java平台,一般是將你的java程式通過平台解析成那種作業系統可以啟動並執行程式,java程式就像黃金,直接用來買東西是不行的,而java的解析器就像是銀行,你將黃金到銀行換成錢就能用了,而作業系統就像國家,你可以把黃金換成美元,人民幣,歐元.......然後在各個地方使用,java也一樣,解析器在不同的環境下將你的java編譯成不同s的程式從而實現跨平台這一功能,但是像MFC就不能和java一樣了,往往只能在windows下使用,就像人民幣在美國不能直接買東西是同一個道理
java線程池同步問題
給你一個思路,設定一個標準flag 值可以為1 2 3,然後 Before線程一直 wait 直到 flag 變為1,執行完後將flag 變為2,MyThread線程一直 wait 直到 flag 變為2,執行完後將flag 變為3,
After線程一直 wait 直到 flag 變為3,執行完後結束。
其實多線程主要用於並發,如果是順序執行,還不如放在一個線程裡