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