摘要:線程的基本狀態(tài)線程的基本操作與內(nèi)存模型線程組守護(hù)線程線程優(yōu)先級(jí)線程安全與隱蔽錯(cuò)誤線程的基本狀態(tài)線程的生命周期線程的基本操作新建線程終止線程立即終止線程所有活動(dòng)方法在結(jié)束線程時(shí)會(huì)直接終止線程并立即釋放這個(gè)線程所持有的鎖可能引起數(shù)據(jù)不一致強(qiáng)烈建
1.線程的基本狀態(tài) 2.線程的基本操作 3. volatile與java內(nèi)存模型 4.線程組 5.守護(hù)線程(Daemon) 6.線程優(yōu)先級(jí) 7.線程安全與synchronized 8.隱蔽錯(cuò)誤1.線程的基本狀態(tài)
線程的生命周期
2.線程的基本操作1.新建線程
Thread tl=new Thread(){ @override public void run(){ System.out.println("Hel1o,I am t1"); }; t1.start(); t1.start();
2.終止線程
Thread.stop() //立即終止線程所有活動(dòng)
stop()方法在結(jié)束線程時(shí),會(huì)直接終止線程,并立即釋放這個(gè)線程所持有的鎖,可能引起數(shù)據(jù)不一致,強(qiáng)烈建議不使用!!
//正確停止線程的方法 public static class ChangeObjectThread extends Thread { volatile boolean stopme = false; //標(biāo)記位,記錄是否要停止線程 public void stopMe(){ stopme = true; } @Override public void run() { while (true) { if (stopme){ //在合適的地方停止線程,避免數(shù)據(jù)不一致 System.out.println("exit by stop me"); break; } synchronized (u) { int v = (int) (System.currentTimeMillis() / 1000); u.setId(v); //Oh, do sth. else try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } u.setName(String.valueOf(v)); } Thread.yield(); } } }
3.線程中斷
public void Thread.interrupt() //中斷線程,僅發(fā)送通知,設(shè)置標(biāo)記位,等待合適時(shí)機(jī)線程中斷 public boolean Thread.isInterrupted() //判斷是否被中斷,判斷標(biāo)記位 public static boolean Thread.interrupted()//判斷是否被中斷,并清除當(dāng)前中斷狀態(tài)
Thread tl=new Thread(){ @Override public void run(){ while(true){ if(Thread.currentThread().isInterrupted()){ //需要設(shè)立中斷處理方法,否則無(wú)法響應(yīng)中斷 System.out.println("Interruted!"); break; ) Thread.yield(); }
public class InterruputSleepThread { public static void main(String[] args) throws InterruptedException { Thread t1=new Thread(){ @Override public void run(){ while(true){ if(Thread.currentThread().isInterrupted()){ System.out.println("Interruted!"); break; } try { Thread.sleep(2000); //由于中斷而拋出異常會(huì)清除中斷標(biāo)記 } catch (InterruptedException e) { System.out.println("Interruted When Sleep"); //sleep被中斷后,重置標(biāo)記位繼續(xù)運(yùn)行,保證數(shù)據(jù)一致性和完整性 //設(shè)置中斷狀態(tài) Thread.currentThread().interrupt(); } Thread.yield(); } } }; t1.start(); Thread.sleep(2000); t1.interrupt(); } }
4.等待(wait)和通知(notify)
public final void wait() throws InterruptedException; public final native void notify();
object.wait()和object.notify()方法必須包含在synchronized關(guān)鍵字中,因?yàn)橐@取對(duì)象的監(jiān)視器(monitor對(duì)象)
public class SimpleWN { final static Object object = new Object(); public static class T1 extends Thread{ public void run() { synchronized (object) { System.out.println(System.currentTimeMillis()+":T1 start! "); try { System.out.println(System.currentTimeMillis()+":T1 wait for object "); object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(System.currentTimeMillis()+":T1 end!"); } } } public static class T2 extends Thread{ public void run() { synchronized (object) { System.out.println(System.currentTimeMillis()+":T2 start! notify one thread"); object.notify(); System.out.println(System.currentTimeMillis()+":T2 end!"); try { Thread.sleep(2000); //此處sleep()2秒,T1此時(shí)仍然需要等待2秒才會(huì)繼續(xù)執(zhí)行 } catch (InterruptedException e) { } } } } public static void main(String[] args) { Thread t1 = new T1() ; Thread t2 = new T2() ; t1.start(); t2.start(); //輸出: //1565249714094:T1 start! //1565249714094:T1 wait for object //1565249714094:T2 start! notify one thread //1565249714094:T2 end! //1565249716094:T1 end! } }
wait()和sleep()的區(qū)別: 1.wait必須在同步條件內(nèi)使用,sleep無(wú)此要求 2.wait釋放鎖,sleep不會(huì)釋放 3.wait為Object內(nèi)的實(shí)例方法,sleep為Thread的靜態(tài)方法 4.wait需要調(diào)用對(duì)象的notify方法喚醒,sleep知道時(shí)間結(jié)束或者被interrupt打斷
5.掛起(suspend)和繼續(xù)執(zhí)行(resume)
廢棄方法,不作介紹!!有需求可使用wait和notify
6.等待線程結(jié)束(join)和謙讓yield
public final void join() throws InterruptedException //一直阻塞線程直至結(jié)束 public final synchronized vold join(long millis)throws InterruptedException //給出最大等待時(shí)間
join的機(jī)制實(shí)際為在方法調(diào)用線程循環(huán)wait,如下join()核心代碼:
while(isAlive()){ wait(0); }
public static native void yield();
yield()方法表示當(dāng)前線程讓出cpu計(jì)算資源,但還是會(huì)參與cpu資源的爭(zhēng)奪,一般用于防止低優(yōu)先級(jí)線程占用太多cpu資源
3. volatile與java內(nèi)存模型volatile關(guān)鍵字可以保證一定的原子性,但是不能替代鎖,不能保證100%線程安全 volatile可以保證可見性和有序性,充當(dāng)內(nèi)存柵欄,禁止指令重拍4.線程組
當(dāng)一個(gè)系統(tǒng)中線程較多且功能分配比較明確是可以使用線程組
public class ThreadGroupName implements Runnable { public static void main(String[] args) { ThreadGroup tg = new ThreadGroup("PrintGroup"); Thread t1 = new Thread(tg, new ThreadGroupName(), "T1"); Thread t2 = new Thread(tg, new ThreadGroupName(), "T2"); t1.start(); t2.start(); System.out.println(tg.activeCount()); //2 tg.list(); //java.lang.ThreadGroup[name=PrintGroup,maxpri=10] // Thread[T1,5,PrintGroup] // Thread[T2,5,PrintGroup] } @Override public void run() { String groupAndName = Thread.currentThread().getThreadGroup().getName() + "-" + Thread.currentThread().getName(); while (true) { System.out.println("I am " + groupAndName); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
activeCount()獲取線程組中的活動(dòng)線程數(shù)
list()則打印線程組中的所有線程信息
守護(hù)線程在后臺(tái)執(zhí)行,如垃圾回收線程,與之對(duì)應(yīng)的為用戶線程,當(dāng)守護(hù)線程要守護(hù)的對(duì)象不存在了,則守護(hù)線程自動(dòng)退出
當(dāng)一個(gè)java應(yīng)用中只有守護(hù)線程時(shí),java虛擬機(jī)自動(dòng)退出
public class DaemonDemo { public static class DaemonT extends Thread{ public void run(){ while(true){ System.out.println("I am alive"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws InterruptedException { Thread t=new DaemonT(); t.setDaemon(true); //此方法必須在start方法前 t.start(); Thread.sleep(2000); } }
上述代碼中 t被設(shè)置為守護(hù)線程,當(dāng)主線程退出時(shí),守護(hù)線程也會(huì)退出;若未設(shè)置為守護(hù)線程,主線程退出后,t線程將依舊打印.
6.線程優(yōu)先級(jí)線程可以設(shè)置優(yōu)先級(jí),優(yōu)先級(jí)高的線程獲取到cpu資源的概率會(huì)更大(但不是一定會(huì)比低優(yōu)先級(jí)的線程先獲取到cpu資源)
public class PriorityDemo { public static class HightPriority extends Thread{ static int count=0; public void run(){ while(true){ synchronized(PriorityDemo.class){ //此處鎖住該類是為了讓產(chǎn)生一次資源競(jìng)爭(zhēng),使優(yōu)先級(jí)差異表現(xiàn)更為明顯 count++; if(count>10000000){ System.out.println("HightPriority is complete"); break; } } } } } public static class LowPriority extends Thread{ static int count=0; public void run(){ while(true){ synchronized(PriorityDemo.class){ count++; if(count>10000000){ System.out.println("LowPriority is complete"); break; } } } } } /** * HightPriority先完成的次數(shù)多,但是 不保證 * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { Thread high=new HightPriority(); LowPriority low=new LowPriority(); high.setPriority(Thread.MAX_PRIORITY); //此方法可以設(shè)置優(yōu)先級(jí) 0-10 數(shù)字 low.setPriority(Thread.MIN_PRIORITY); low.start(); high.start(); } }
試驗(yàn)多次HightPriority總是能比LowPriority快,但是不保證一定是這樣.7.線程安全與synchronized
synchronized為重量級(jí)同步鎖,實(shí)現(xiàn)線程間的同步,sychronize鎖住的對(duì)象、方法和代碼塊,每一次只能有一個(gè)線程進(jìn)行訪問(wèn),從而保證了線程的安全 用法: 指定鎖對(duì)象: synchronized(object){.....} 指定實(shí)例方法 : public synchronized void println() 指定靜態(tài)方法: public static synchronized void println()
public class BadAccountingSync2 implements Runnable{ static int i=0; public static synchronized void increase(){ //此處如果不適用static則為線程不安全,最終i值必定小于20000 i++; } @Override public void run() { for(int j=0;j<10000;j++){ increase(); } } public static void main(String[] args) throws InterruptedException { Thread t1=new Thread(new BadAccountingSync2()); Thread t2=new Thread(new BadAccountingSync2()); t1.start();t2.start(); t1.join();t2.join(); System.out.println(i); //輸出20000 } }8.隱蔽錯(cuò)誤
數(shù)值溢出
int v1=1073741827; int v2=1431655768; System.out,println("vl="+v1); //1073741827 System.out.println("v2="+v2); //1431655768 int ave=(vl+v2)/2; System.out,println("ave="+ave); //-894784850 數(shù)值溢出,兩int數(shù)值相加超過(guò)int最大值時(shí)溢出
并發(fā)不安全的arraylist
public class ArrayListMultiThread { static ArrayListal = new ArrayList (10); public static class AddThread implements Runnable { @Override public void run() { for (int i = 0; i < 1000000; i++) { al.add(i); } } } public static void main(String[] args) throws InterruptedException { Thread t1=new Thread(new AddThread()); Thread t2=new Thread(new AddThread()); t1.start(); t2.start(); t1.join();t2.join(); System.out.println(al.size()); } }
以上程序可能會(huì)有三個(gè)結(jié)果 1.正常結(jié)束,打印值為2000000 (運(yùn)氣好) 2.正常結(jié)束,打印值小于2000000 (一般情況下會(huì)出現(xiàn)這種并發(fā)不安全的現(xiàn)象) 3.拋出異常,arrayindexoutofbound(擴(kuò)容過(guò)程中內(nèi)部一致性遭破壞,導(dǎo)致數(shù)組越界) 如何避免此問(wèn)題? 使用線程安全的vector代替arraylist
并發(fā)不安全的hashmap
public class JDK8HashMapMultiThread { static Mapmap = new HashMap (); static int nullNum = 0; //記錄空值數(shù) public static class AddThread implements Runnable { int start = 0; public AddThread(int start) { this.start = start; } @Override public void run() { for (int i = start; i < 10000; i += 2) { map.put(Integer.toString(i), Integer.toString(i)); } } } public static void test() throws Exception { Thread t1 = new Thread(new JDK8HashMapMultiThread.AddThread(0)); Thread t2 = new Thread(new JDK8HashMapMultiThread.AddThread(1)); t1.start(); t2.start(); t1.join(); t2.join(); } public static void main(String[] args) throws Exception { test(); for (int i = 0; i < 10000; i++) { if (map.get("" + i) == null) { nullNum += 1; } } System.out.println(10000 - nullNum); //hashmap中存有的entry數(shù) System.out.println(map.size()); //hashmap中size屬性的值 } }
打印值為: 6573 9715 打印結(jié)果不一定,但可以看出線程一定不安全,原因在于: 1.put時(shí),兩個(gè)entry的key hash值一致,同時(shí)進(jìn)入插入代碼,導(dǎo)致只有一個(gè)值插入成功 2.插入成功后size數(shù)值 進(jìn)行size++,非原子操作,導(dǎo)致并發(fā)不安全 注意: 另網(wǎng)上有其他博文說(shuō)會(huì)產(chǎn)生resize閉鏈問(wèn)題,此問(wèn)題只存在jdk1.8版本以前,1.8版本已優(yōu)化過(guò)hashmap,故不存在閉鏈問(wèn)題 想要避免并發(fā)問(wèn)題可以使用concurrenthashmap
錯(cuò)誤的加鎖
public class BadLockOnInteger implements Runnable{ public static Integer i=0; //integer為不變類型,對(duì)其加鎖無(wú)效 static BadLockOnInteger instance=new BadLockOnInteger(); @Override public void run() { for(int j=0;j<1000000;j++){ synchronized(i){ // synchronized(instance){ //改為對(duì)instance加鎖即線程安全了 i++; } } } public static void main(String[] args) throws InterruptedException { Thread t1=new Thread(instance); Thread t2=new Thread(instance); t1.start();t2.start(); t1.join();t2.join(); System.out.println(i); //打印值小于2000000 } }
注意: 對(duì)于不變對(duì)象如基本類型 Integer String 等加鎖,相當(dāng)于每次都鎖不同的對(duì)象,從而導(dǎo)致并發(fā)問(wèn)題產(chǎn)生.
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/75952.html
摘要:在本例中,講述的無(wú)鎖來(lái)自于并發(fā)包我們將這個(gè)無(wú)鎖的稱為。在這里,我們使用二維數(shù)組來(lái)表示的內(nèi)部存儲(chǔ),如下變量存放所有的內(nèi)部元素。為什么使用二維數(shù)組去實(shí)現(xiàn)一個(gè)一維的呢這是為了將來(lái)進(jìn)行動(dòng)態(tài)擴(kuò)展時(shí)可以更加方便。 我們已經(jīng)比較完整得介紹了有關(guān)無(wú)鎖的概念和使用方法。相對(duì)于有鎖的方法,使用無(wú)鎖的方式編程更加考驗(yàn)一個(gè)程序員的耐心和智力。但是,無(wú)鎖帶來(lái)的好處也是顯而易見的,第一,在高并發(fā)的情況下,它比有鎖...
摘要:有時(shí)候,由于初期考慮不周,或者后期的需求變化,一些普通變量可能也會(huì)有線程安全的需求。它可以讓你在不改動(dòng)或者極少改動(dòng)原有代碼的基礎(chǔ)上,讓普通的變量也享受操作帶來(lái)的線程安全性,這樣你可以修改極少的代碼,來(lái)獲得線程安全的保證。 有時(shí)候,由于初期考慮不周,或者后期的需求變化,一些普通變量可能也會(huì)有線程安全的需求。如果改動(dòng)不大,我們可以簡(jiǎn)單地修改程序中每一個(gè)使用或者讀取這個(gè)變量的地方。但顯然,這...
摘要:大家好,我是冰河有句話叫做投資啥都不如投資自己的回報(bào)率高。馬上就十一國(guó)慶假期了,給小伙伴們分享下,從小白程序員到大廠高級(jí)技術(shù)專家我看過(guò)哪些技術(shù)類書籍。 大家好,我是...
閱讀 2584·2021-11-15 11:37
閱讀 2709·2021-09-23 11:21
閱讀 3012·2021-09-07 10:11
閱讀 3212·2019-08-30 15:53
閱讀 2870·2019-08-29 15:13
閱讀 1647·2019-08-26 13:57
閱讀 1146·2019-08-26 12:23
閱讀 2483·2019-08-26 11:51