第十六天(高級線程part 1)

        這個筆記……嘔心咧血:從晚上十點到凌晨三點,共十頁。原來這麼丁點內容可以寫這麼長時間。我以為這才是學習,該略過的還是要略過,該著重理解的還是要花時間下去。其中費比較長時間的是對某個類中的方法是使用條件和效果,這是靠寫程序去測試的。要知道寫程序是比較費時間的一個過程。

還有要說明是,過了凌晨零時就是第二天,所以這篇筆記應該算是今天的。而“真正”今天到筆記卻是不能給到 ,原因是沒時間。

另一點要說明的是快要考試了,而學校的那本極品java教材,什麽都講,就是什麽都講不好,一頭霧水,我不得不借編程指南來理解。而問題又來了,編程指南的寫作順序與那本極品教材完全不同,所以我只能跳過指南的某些章節來寫筆記。所以明天起,雖然只寫了一部份的高級線程,還是先暫擱一下,寫期末考試要考的內容筆記。

2011-06-14(Advanced Thread)
1、線程池。目前為止,若想啟動一個線程,首先要創建線程對象,而且人物執行完畢后線程死亡,不再啟動。創建線程對象和清除已執行完畢的線程都會佔用大量的CPU資源。
如果在系統中開闢一個區域來存放一些待命的線程,如果需要執行任務,進入某個待命線程執行,結束之後退出以留給其他要執行的人物位置。這樣整個過程不需進行創建線程對象以及這個對象用完之後的清理工作。這個區域稱為線程池。線程池從J2SE5.0開始有。有了線程池后,就多了個管理線程池的東西,可以簡單的控制線程執行(比如應該多少個線程執行,哪個線程要延遲一點執行)。既然是個“池”,當然是有大小的:
(1)固定尺寸的線程池。這種線程池優點是實現簡單。但是固定尺寸后很容易滿足不了實際的狀況:對於某個尺寸的線程池,如果要執行的任務很多,會造成大量的再線程池外面等待的線程;如果很少,線程池內的待命的線程會很多,造成內存的浪費;
(2)可變尺寸的線程池。如果任務少則縮減待命線程數量;任務多則擴充待命線程數。這種擴充縮減行為都是計算機完成的。缺點是實現起來複雜很多。
2、線程池對象的創建。線程的相關類、接口在java.util.concurrent中。其中的Executors(執行者)類中的靜態工廠方法可以創建線程池對象:
                               public static ExecutorService newFixedThreadPool(int nThreads)
創建具有固定大小的線程池。
                               public static ExceutorService newSingleThreadExecutor()
創建一個線程池,在同一時刻只能執行一個任務,使其按可以保證多個任務是按順序執行的。不可重新設置為支持多個線程。
上兩個靜態工廠方法都是ExecutorService接口類型的引用,指向的是線程池對象。以後會說到,線程池有很多種,不同的接口執行不同種的線程池。既然現在不通過Thread來創建線程對象,就不能調用其中的start()方法來啟動線程。下面的ExecutorService接口中方法(這個方法繼承Executor接口)代替了start:
                                               public void executor(Runnable command)
3、固定尺寸線程池的使用。
I、自定義固定尺寸線程池。給個例子: 結果:
                                                                                                             Thread 1>>>Start
                                                                                                              Thread 2>>>Start
                                                                                                              Thread 1 Executing
                                                                                                              Thread 2 Executing
                                                                                                              Thread 1>>>End
                                                                                                              Thread 2>>>End
                                                                                                              Thread 3>>>Start
                                                                                                              Thread 3 Executing
                                                                                                              Thread 3>>>End
①這裡只需創建線程池和實現Runnable接口的類的對象,不是把對象放進線程池中,而是通過execute方法將線程和線程池關聯;
②注意執行結果,只有當線程池內有待命線程,外面等待的線程才會啟動。在本例,Thread 1和Thrad 2當中的某一個執行完,Thread 3才會執行;
③當所有線程執行完后,程序並沒有退出JVM。原因是線程池是只負責存儲待命線程,當外面的沒有線程在等待(即所有線程執行完),它依然在等待線程進來。可通過關閉線程池來退出JVM,關閉線程池的方法:
                                                      public void shutdown() //無等待線程時關閉線程池
                                                     
public List shoudownNow() //無論是否所有任務執行完,立即關閉線程池。返回等待中的任務
兩種方法都是在全部線程使用完execute方法后使用,否則會拋出異常。區別是:shutdown是線程池外無等待線程時,關閉線程池;shutdownNow,這裡不好說,打個比方:線程池只開放一次,外面的線程盡可能進來(盡可能:線程數大於或等於容量則進來容量個線程,小於則進來所有線程),執行完后,不管外面有無線程,立即關閉線程池。對上面的程序做如下修改:
①19行后加入pool.shutdown();②19行后加入pool.shutdownNow();③在② 的基礎上改容量為4。
結果:①退出了JVM;②:
                                                                                      Thread 1>>>Start
                                                                                      Thread 2>>>Start
                                                                                      Thread 1 Executing
                                                                                       Thread 2 Executing
                                                                                       Thread 1>>>End
                                                                                       Thread 2>>>End
③結果同②。
由shutdownNow()的性質來看,對於單任務線程池,會在執行第一個線程后關閉線程池,退出JVM;對於可變尺寸線程池,則會與shutdown一樣效果。
II、單任務線程池。將上面的程序的線程池創建語句改為:
                              ExecutorService pool=Executors.newSingleThreadExecutor ();
則結果為:
                                                                                      Thread 1>>>Start
                                                                                      Thread 1 Executing
                                                                                      Thread 1>>>End
                                                                                      Thread 2>>>Start
                                                                                      Thread 2 Executing
                                                                                      Thread 2>>>End
                                                                                      Thread 3>>>Start
                                                                                      Thread 3 Executing
                                                                                      Thread 3>>>End
所以這個與設置尺寸為1的線程池相似。
4、可變尺寸線程池的使用。可變尺寸線程池可用如下Executor類中的靜態工廠方法:
                                                                        public static ExecytorService newCachedThreadPool();
如,將上面的程序的線程池創建語句改為:
                                                                         ExecutorService pool=Executors.newCachedThreadPool ();
則結果為:
                                                                                   Thread 1>>>Start
                                                                                    Thread 3>>>Start
                                                                                    Thread 2>>>Start
                                                                                    Thread 3 Executing
                                                                                    Thread 1 Executing
                                                                                    Thread 3>>>End
                                                                                    Thread 2 Executing
                                                                                    Thread 2>>>End
                                                                                    Thread 1>>>End
有此可見,3個任務是幷發進行的,自動調整待命線程的數量,優化執行性能。這個與用Thread類來執行線程差不多。
5、延遲線程池的使用。創建這種線程池,也是用Executors類中的靜態工廠方法:
                                    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
                                    public static ScheduledExecutorService newSingleThreadScheduledExecutor()

這兩個方法的返回類型均為ScheduledExecutorService型,是一個繼承ExecutorService接口的接口。當然這個接口又另一個替代start開啟線程的方法:
                                      public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit)
command表示要啟動的線程,delay表示延期時間,unit是延期時間單位。TimeUnit是一個枚舉,TimtUnit.NANOSECOND、TimeUnit.MICROSECOND、TimeUnit.MILLISECOND、TimeUnit.SECOND分別表示納秒、微秒、毫秒、秒。 結果如下: 
                                                                              1307988596500
                                                                              1307988597515
                                                                              Thread_1>>>Start
                                                                              Thread_1 Executing
                                                                              Thread_1>>>End
                                                                              1307988598515
                                                                              Thread_2>>>Start
                                                                              Thread_2 Executing
                                                                              Thread_2>>>End
注意這裡的延遲時間是指主線程開始時間到線程啟動的之間時間間隔。
6、使用自定義參數線程池。下面這種線程池有多個參數,滿足某些特殊需求。這種線程池對象的來自于ThreadPoolExecutor類:
                         public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long                            keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
corePoolSize稱為標準尺寸,maximumPoolSize稱為最大尺寸,keepAliveTime是空閒線程存活時間,unit時間單位,workQueue阻塞隊列,書裡沒有詳細講,這裡當作一個整數處理。三個整數:corePoolSize、maxPoolSize、workQueue決定了這個自定義線程池的行為準則(來自API):
(1)ThreadPoolExecutor 将根据 corePoolSize和 maximumPoolSize设置的边界自动调整池大小。当新任务在方法 execute(java.lang.Runnable) 中提交时:
①如果运行的线程少于 corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的;
②如果运行的线程多于 corePoolSize 而少于 maximumPoolSize,则仅当队列满时才创建新线程;
③如果设置的 corePoolSize 和 maximumPoolSize 相同,则创建了固定大小的线程池;
④如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务。在大多数情况下,核心和最大池大小仅基于构造来设置,不过也可以使用 setCorePoolSize(int) 和 setMaximumPoolSize(int) 进行动态更改。
(2)所有 BlockingQueue 都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:
①如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队;
②如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程;
③如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。
ThreadPoolExecutor幾個常用方法:
(1)public int getCoreSize():返回線程池的標準尺寸;
(2)public int getActiveCount():返回線程池中正在執行任務的線程數;
(3)public int getPoolSize():返回當前尺寸;
(4)public int getMaximumPoolSize():返回線程池的最大尺寸;
(5)public BlockingQueue getQueue():返回線程池工作等待隊列。
例: 輸出為:
                                                                                1
                                                                                Thread 1>>>Start 
                                                                                Thread 1 Executing
                                                                                Thread 1>>>End
                                                                                Thread 2>>>Start
                                                                                Thread 2 Executing
                                                                                Thread 2>>>End
                                                                                Thread 3>>>Start
                                                                                Thread 3 Executing
                                                                                Thread 3>>>End
按照上面的規則,任務mt[0]遞交,小於標準尺寸,直接執行;任務mt[1]遞交,大於等於標準尺寸,排隊;任務mt[2]遞交,同mt[1],排隊,當前一個執行完後再執行。所以線程池實際的大小為1。將工作等待序列(第16行)長度改為1,結果為:
                                                                              2
                                                                              Thread 3>>>Start
                                                                              Thread 1>>>Start
                                                                              Thread 3 Executing
                                                                              Thread 1 Executing
                                                                               Thread 3>>>End
                                                                               Thread 1>>>End
                                                                               Thread 2>>>Start
                                                                               Thread 2 Executing
                                                                               Thread 2>>>End
任務mt[0]遞交,小於標準尺寸,直接執行;任務mt[1]遞交,等於標準尺寸,排隊;任務mt[2]遞交,且隊列已滿,使用備用待命線程;當任務mt[0]執行完,隊列中任務mt[1]再執行,所以是1>>3>>1的順序,實際線程池大小為2。
7、有返回值的線程。我們知道,實現Runnable接口的線程要重寫run(),而這個run是沒有返回值的。實際中希望線程執行后能有返回值。這是可以實現的,需要Callable與Futrue接口。兩個接口都位於java.util.concurrent包中。
I、Callable接口。與Runnable接口類似,實現這個接口的需要實現call方法:
                                                                                      public Object call() throws Exception
無論返回值為什麽類型,系統都會作Object處理。當然,可以使用泛型指定放回類型。
II、Future接口。需要實現接口Callable的類的對象所表示的任務是,需要通過調用ExecutorService接口的submit方法來完成:
                                                                                      public Future submit(Callable task)
Future中的方法:
(1)public boolean cacel(boolean mayInterrupIfRunning):
试图取消对此任务的执行。如果任务已完成、或已取消,或者由于某些其他原因而无法取消,则此尝试将失败。当调用 cancel 时,如果调用成功,而此任务尚未启动,则此任务将永不运行。如果任务已经启动,则 mayInterruptIfRunning 参数确定是否应该以试图停止任务的方式来中断执行此任务的线程。
此方法返回后,对 isDone() 的后续调用将始终返回 true。如果此方法返回 true,则对 isCancelled() 的后续调用将始终返回 true。
(2)puhlic Object get()throws InterruptedException,ExecutionException:等待任務結束,獲得返回值;
(3)public Object get(long timeout,TimeUnit unit)throws InterruptedException,ExecutionException,TimeoutException:等待timeout這麼長的時間,如果在這時間內任務由於某種原因未完成而未能獲取返回值,則拋出TimeoutException異常;
(4)public boolean isCanclled():看任務是否被終止;
(5)public boolean isDone():判斷任務是否完成。
例: 結果:  
                                                                               Thread 1>>>Start
                                                                               Thread 1>>>End
                                                                                Finished
如果將註釋去掉,則結果為:
                                                                                Thread 1>>>Start
                                                                                Timeout
                                                                                Thread 1>>>End
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值