創(chuàng)建線程的三種方式:
- 繼承Thread類
語法:public class A extends Thread{}
案例:A a=new A();a.start();
- 實(shí)現(xiàn)Runnable接口
語法:public class A implements Runnable{}
案例:A a=new A();Thread t=new Thread(a);t.start()
- 實(shí)現(xiàn)了Callable接口(有返回值的線程)
語法:public class CallableDemo implements Callable<Integer>
案例:
ExecutorService es = Executors.newSingleThreadExecutor(); //創(chuàng)建線程池
CallableDemo calTask=new CallableDemo();//創(chuàng)建Callable對象任務(wù)
Future<Integer> future =es.submit(calTask);//提交任務(wù)并獲取執(zhí)行結(jié)果
future.get()//輸出返回來的值
線程安全之“鎖”:
01-synchronized
synchronized保證代碼具有原子性和可見性递胧,可見性保證每次讀取到的是最新值,原子性保證在整個(gè)操作過程中確保代碼執(zhí)行的原子性,不可分割逃沿。
02-ReentrantLock
ReentrantLock
是一個(gè)可重入的互斥鎖懂算,又被稱為“獨(dú)占鎖”距贷。顧名思義轩端,ReentrantLock鎖
在同一個(gè)時(shí)間點(diǎn)只能被一個(gè)線程鎖持有党涕;而可重入的意思是活烙,ReentrantLock鎖
,可以被單個(gè)線程多次獲取遣鼓。
“公平鎖”和“非公平鎖”:ReentrantLock分為“公平鎖”和“非公平鎖”啸盏。它們的區(qū)別體現(xiàn)在獲取鎖的機(jī)制上是否公平∑锼睿“鎖”是為了保護(hù)競爭資源回懦,防止多個(gè)線程同時(shí)操作線程而出錯(cuò),ReentrantLock在同一個(gè)時(shí)間點(diǎn)只能被一個(gè)線程獲取(當(dāng)某線程獲取到“鎖”時(shí)次企,其它線程就必須等待)怯晕;ReentraantLock是通過一個(gè)FIFO的等待隊(duì)列來管理獲取該鎖所有線程的。在“公平鎖”的機(jī)制下缸棵,線程依次排隊(duì)獲取鎖舟茶;而“非公平鎖”在鎖是可獲取狀態(tài)時(shí),不管自己是不是在隊(duì)列的開頭都會獲取鎖堵第。
使用方式:
創(chuàng)建鎖對象:Lock lock = new ReentrantLock();
鎖代碼:lock.lock();.......鎖的內(nèi)容贿堰;.......lock.unlock();
常用方法:
(1)嘗試獲取鎖:boolean locked = lock.tryLock();//根據(jù)locked值去判定執(zhí)行什么樣的代碼
(2)嘗試超時(shí):boolean locked=lock.tryLock(5, TimeUnit.SECONDS);//嘗試獲取lock鎖對象5秒
(3)可打斷:lock.lockInterruptibly(),線程在請求lock并被阻塞時(shí)(t1占用了lock鎖對象赞咙,t2請求被阻塞)颊亮,如果此時(shí)t2被t3線程interrupt(t2.interrupt())宿饱,則“此t2線程會被喚醒并被要求處理InterruptedException”。
通俗理解:
(1)lock(), 拿不到lock就不罷休针余,不然線程就一直block饲鄙。 比較無賴的做法。不能被打斷
(2)tryLock()圆雁,馬上返回忍级,拿到lock就返回true,不然返回false伪朽。 比較瀟灑的做法轴咱。
帶時(shí)間限制的tryLock(),拿不到lock,就等一段時(shí)間嗦玖,超時(shí)返回false患雇。比較聰明的做法跃脊。
(3)lock.lockInterruptibly()請求鎖宇挫,請求不到也會一直請求,但是可以被其他線程打斷酪术,打斷后處理InterruptedException異常器瘪,即執(zhí)行catch和finally代碼塊
03-synchronized和ReentrantLock的區(qū)別
(1)synchronized的鎖是自動(dòng)釋放的,而ReentrantLock得lock需要手動(dòng)釋放(lock.unlock())绘雁;
(2)使用synchronized鎖定的話如果遇到異常橡疼,jvm會自動(dòng)釋放鎖,但是lock必須手動(dòng)釋放鎖庐舟,因此經(jīng)常在finally中進(jìn)行鎖的釋放欣除;
(3)ReentrantLock更具靈活性
常見線程池:
線程池
是一種多線程
處理形式,處理過程中將任務(wù)添加到隊(duì)列挪略,然后在創(chuàng)建線程后自動(dòng)啟動(dòng)這些任務(wù)历帚。線程池線程都是后臺線程。
01-newFixedThreadPool(6)
案例:ExecutorService service = Executors.newFixedThreadPool(6);
解讀:來一個(gè)任務(wù)杠娱,創(chuàng)建一個(gè)線程挽牢,線程啟動(dòng)后不會銷毀,上限是6個(gè)摊求。當(dāng)線程被占用完時(shí)禽拔,所有任務(wù)都被放在一個(gè)隊(duì)列中進(jìn)行等待,等待線程被釋放室叉。
02-newCachedThreadPool()
案例:ExecutorService service = Executors.newCachedThreadPool();
解讀:剛開始一個(gè)線程都沒有睹栖,來一個(gè)任務(wù)就起一個(gè)線程,當(dāng)來新任務(wù)時(shí)發(fā)現(xiàn)有空閑的線程茧痕,就直接使用空閑的磨淌,不再新起線程了(線程空閑1min就自動(dòng)銷毀);當(dāng)來新任務(wù)時(shí)發(fā)現(xiàn)沒有空閑的線程凿渊,此時(shí)便啟動(dòng)一個(gè)新的線程梁只,保證在等待區(qū)任務(wù)隊(duì)列中的任務(wù)個(gè)數(shù)永遠(yuǎn)為0;
03-newSingleThreadExecutor()
案例:ExecutorService service = Executors.newSingleThreadExecutor();
解讀:線程池永遠(yuǎn)只有一個(gè)線程埃脏。保證任務(wù)的先后執(zhí)行順序搪锣,只有第一個(gè)任務(wù)執(zhí)行完,才能執(zhí)行第二個(gè)任務(wù)彩掐。線程起來后永遠(yuǎn)不結(jié)束构舟。
04-newScheduledThreadPool(4);
案例:ScheduledExecutorService service = Executors.newScheduledThreadPool(4);
解讀:執(zhí)行定時(shí)的任務(wù)。線程是可以復(fù)用的堵幽,第一個(gè)線程來了狗超,執(zhí)行完后弹澎,來第二個(gè)線程,這時(shí)候就直接用已存在的空閑線程努咐。定時(shí)容器裝任務(wù)苦蒿,線程啟動(dòng)后就不會銷毀。service.scheduleAtFixedRate(task, 0, 500, TimeUnit.MILLISECONDS);第一個(gè)tast立即執(zhí)行渗稍,沒隔500mms執(zhí)行一次任務(wù)佩迟。
05-newWorkStealingPool();
案例:ExecutorService service = Executors.newWorkStealingPool();
解讀:工作竊取,每個(gè)線程維護(hù)自己的等待隊(duì)列竿屹,當(dāng)自己隊(duì)列為空時(shí)报强,會去偷其他線程隊(duì)列中的任務(wù)。
06-new ForkJoinPool()
作用:將一個(gè)任務(wù)進(jìn)行拆分處理拱燃,并將處理后的結(jié)果進(jìn)行匯總
案例:
ForkJoinPool fjp = new ForkJoinPool();
執(zhí)行分塊的類秉溉,需要extends RecursiveTask或RecursiveAction,其中:AddTask extends RecursiveAction:無返回值碗誉;AddTask extends RecursiveTask :有返回值召嘶,通過task.join()加入主線程匯總求和。
分塊:AddTask task = new AddTask(0, nums.length);
執(zhí)行分塊計(jì)算:fjp.execute(task);
解讀:工作竊取诗充,每個(gè)線程維護(hù)自己的等待隊(duì)列苍蔬,當(dāng)自己隊(duì)列為空時(shí),會去偷其他線程隊(duì)列中的任務(wù)蝴蜓。