摘要:線程的基本狀態(tài)線程的基本操作與內(nèi)存模型線程組守護線程線程優(yōu)先級線程安全與隱蔽錯誤線程的基本狀態(tài)線程的生命周期線程的基本操作新建線程終止線程立即終止線程所有活動方法在結(jié)束線程時會直接終止線程并立即釋放這個線程所持有的鎖可能引起數(shù)據(jù)不一致強烈建
1.線程的基本狀態(tài) 2.線程的基本操作 3. volatile與java內(nèi)存模型 4.線程組 5.守護線程(Daemon) 6.線程優(yōu)先級 7.線程安全與synchronized 8.隱蔽錯誤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() //立即終止線程所有活動
stop()方法在結(jié)束線程時,會直接終止線程,并立即釋放這個線程所持有的鎖,可能引起數(shù)據(jù)不一致,強烈建議不使用!!
//正確停止線程的方法 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)記位,等待合適時機線程中斷 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è)立中斷處理方法,否則無法響應(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); //由于中斷而拋出異常會清除中斷標(biāo)記 } catch (InterruptedException e) { System.out.println("Interruted When Sleep"); //sleep被中斷后,重置標(biāo)記位繼續(xù)運行,保證數(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)鍵字中,因為要獲取對象的監(jiān)視器(monitor對象)
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此時仍然需要等待2秒才會繼續(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無此要求 2.wait釋放鎖,sleep不會釋放 3.wait為Object內(nèi)的實例方法,sleep為Thread的靜態(tài)方法 4.wait需要調(diào)用對象的notify方法喚醒,sleep知道時間結(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 //給出最大等待時間
join的機制實際為在方法調(diào)用線程循環(huán)wait,如下join()核心代碼:
while(isAlive()){ wait(0); }
public static native void yield();
yield()方法表示當(dāng)前線程讓出cpu計算資源,但還是會參與cpu資源的爭奪,一般用于防止低優(yōu)先級線程占用太多cpu資源
3. volatile與java內(nèi)存模型volatile關(guān)鍵字可以保證一定的原子性,但是不能替代鎖,不能保證100%線程安全 volatile可以保證可見性和有序性,充當(dāng)內(nèi)存柵欄,禁止指令重拍4.線程組
當(dāng)一個系統(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()獲取線程組中的活動線程數(shù)
list()則打印線程組中的所有線程信息
守護線程在后臺執(zhí)行,如垃圾回收線程,與之對應(yīng)的為用戶線程,當(dāng)守護線程要守護的對象不存在了,則守護線程自動退出
當(dāng)一個java應(yīng)用中只有守護線程時,java虛擬機自動退出
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è)置為守護線程,當(dāng)主線程退出時,守護線程也會退出;若未設(shè)置為守護線程,主線程退出后,t線程將依舊打印.
6.線程優(yōu)先級線程可以設(shè)置優(yōu)先級,優(yōu)先級高的線程獲取到cpu資源的概率會更大(但不是一定會比低優(yōu)先級的線程先獲取到cpu資源)
public class PriorityDemo { public static class HightPriority extends Thread{ static int count=0; public void run(){ while(true){ synchronized(PriorityDemo.class){ //此處鎖住該類是為了讓產(chǎn)生一次資源競爭,使優(yōu)先級差異表現(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)先級 0-10 數(shù)字 low.setPriority(Thread.MIN_PRIORITY); low.start(); high.start(); } }
試驗多次HightPriority總是能比LowPriority快,但是不保證一定是這樣.7.線程安全與synchronized
synchronized為重量級同步鎖,實現(xiàn)線程間的同步,sychronize鎖住的對象、方法和代碼塊,每一次只能有一個線程進行訪問,從而保證了線程的安全 用法: 指定鎖對象: synchronized(object){.....} 指定實例方法 : 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.隱蔽錯誤
數(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ù)值相加超過int最大值時溢出
并發(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()); } }
以上程序可能會有三個結(jié)果 1.正常結(jié)束,打印值為2000000 (運氣好) 2.正常結(jié)束,打印值小于2000000 (一般情況下會出現(xiàn)這種并發(fā)不安全的現(xiàn)象) 3.拋出異常,arrayindexoutofbound(擴容過程中內(nèi)部一致性遭破壞,導(dǎo)致數(shù)組越界) 如何避免此問題? 使用線程安全的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時,兩個entry的key hash值一致,同時進入插入代碼,導(dǎo)致只有一個值插入成功 2.插入成功后size數(shù)值 進行size++,非原子操作,導(dǎo)致并發(fā)不安全 注意: 另網(wǎng)上有其他博文說會產(chǎn)生resize閉鏈問題,此問題只存在jdk1.8版本以前,1.8版本已優(yōu)化過hashmap,故不存在閉鏈問題 想要避免并發(fā)問題可以使用concurrenthashmap
錯誤的加鎖
public class BadLockOnInteger implements Runnable{ public static Integer i=0; //integer為不變類型,對其加鎖無效 static BadLockOnInteger instance=new BadLockOnInteger(); @Override public void run() { for(int j=0;j<1000000;j++){ synchronized(i){ // synchronized(instance){ //改為對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 } }
注意: 對于不變對象如基本類型 Integer String 等加鎖,相當(dāng)于每次都鎖不同的對象,從而導(dǎo)致并發(fā)問題產(chǎn)生.
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/75952.html
摘要:在本例中,講述的無鎖來自于并發(fā)包我們將這個無鎖的稱為。在這里,我們使用二維數(shù)組來表示的內(nèi)部存儲,如下變量存放所有的內(nèi)部元素。為什么使用二維數(shù)組去實現(xiàn)一個一維的呢這是為了將來進行動態(tài)擴展時可以更加方便。 我們已經(jīng)比較完整得介紹了有關(guān)無鎖的概念和使用方法。相對于有鎖的方法,使用無鎖的方式編程更加考驗一個程序員的耐心和智力。但是,無鎖帶來的好處也是顯而易見的,第一,在高并發(fā)的情況下,它比有鎖...
摘要:有時候,由于初期考慮不周,或者后期的需求變化,一些普通變量可能也會有線程安全的需求。它可以讓你在不改動或者極少改動原有代碼的基礎(chǔ)上,讓普通的變量也享受操作帶來的線程安全性,這樣你可以修改極少的代碼,來獲得線程安全的保證。 有時候,由于初期考慮不周,或者后期的需求變化,一些普通變量可能也會有線程安全的需求。如果改動不大,我們可以簡單地修改程序中每一個使用或者讀取這個變量的地方。但顯然,這...
摘要:大家好,我是冰河有句話叫做投資啥都不如投資自己的回報率高。馬上就十一國慶假期了,給小伙伴們分享下,從小白程序員到大廠高級技術(shù)專家我看過哪些技術(shù)類書籍。 大家好,我是...
閱讀 2348·2021-11-15 11:37
閱讀 2625·2021-09-23 11:21
閱讀 2952·2021-09-07 10:11
閱讀 3164·2019-08-30 15:53
閱讀 2826·2019-08-29 15:13
閱讀 1606·2019-08-26 13:57
閱讀 1098·2019-08-26 12:23
閱讀 2438·2019-08-26 11:51