摘要:如果把注釋去掉,則在所以非線程都結束時,自動終止。默認所有從線程產生的線程也是線程。停止線程線程完成方法后,就進入狀態。被標示為的區塊會被監控,任何線程要執行區塊都必須先獲得指定的對象鎖定。
Tread和Runnable 定義線程
實現Runnable接口,重寫run()方法
繼承Thread類,重寫run()方法
啟動線程Runnable tortoise=new Tortoise(); Thread thread1=new Thread(tortoise); thread1.start();
Thread thread1=new Tortoise(); thread1.start();線程生命周期 daemon線程
主線程會從main()方法開始執行,直到main()方法結束后停止jvm.如果主線程中啟動了額外線程,則主線程默認會等到被啟動的所有額外線程都執行完run()方法才會終止jvm.
public class Daemon implements Runnable{ @Override public void run() { while(true){ System.out.println("TheViper"); } } public static void main(String[] args){ Thread thread=new Thread(new Daemon()); //thread.setDaemon(true); thread.start(); } }
TheViper TheViper TheViper ...
控制臺會不停的打印。如果把注釋去掉,thread.setDaemon(true),則在所以非daemon線程都結束時,jvm自動終止。
默認所有從daemon線程產生的線程也是daemon線程。
啟動線程后,基本狀態可分為可執行(runnable),阻塞(blocked),執行中(running).
在一個時間點上,一個cpu只能執行一個線程,只是cpu會不斷切換線程。可以用Thread的setPriority()方法設置線程的優先級,設定值1-10,默認為5.優先級越高的線程,越有機會搶占cpu.
線程進入blocked狀態可能的情況:
調用Thread.sleep()方法
進入synchronized前,對競爭對象的鎖定
調用wait()方法阻斷
等待io完成
...
一個處于blocked狀態的線程,可由另一個線程調用interrupt()方法,讓它離開blocked狀態。
public class Daemon implements Runnable{ @Override public void run() { System.out.println("TheViper"); try { Thread.sleep(3000);//進入blocked狀態 } catch (InterruptedException e) { System.out.println("離開blocked狀態"); } } public static void main(String[] args){ Thread thread=new Thread(new Daemon()); thread.start(); thread.interrupt(); } }
TheViper 離開blocked狀態安插線程
假設線程A正在運行,這時線程B想占用cpu運行,等它運行完后,繼續運行線程A.這種情況可以使用join()方法.
未使用join()
public class Daemon implements Runnable{ @Override public void run() { System.out.println("線程開始"); try { Thread.sleep(3000); System.out.println("線程執行"); } catch (InterruptedException e) { System.out.println("離開blocked狀態"); } System.out.println("線程結束"); } public static void main(String[] args){ Thread thread=new Thread(new Daemon()); thread.start(); System.out.println("主線程開始"); } }
主線程開始 線程開始 線程執行 線程結束
使用join()
public class Daemon implements Runnable{ @Override public void run() { System.out.println("線程開始"); try { Thread.sleep(3000); System.out.println("線程執行"); } catch (InterruptedException e) { System.out.println("離開blocked狀態"); } System.out.println("線程結束"); } public static void main(String[] args) throws InterruptedException{ Thread thread=new Thread(new Daemon()); thread.start(); thread.join(); System.out.println("主線程開始"); } }
線程開始 線程執行 線程結束 主線程開始
程序啟動后就開始運行主線程(因為新建線程使用了sleep(),讓新建線程進入blocked狀態,主線程可以占用cpu),對新建的線程使用join(),將其加入到主線程后,原本應該一來就運行的主線程,現在需要等到后面新建的線程執行完后,才能繼續執行。
如果加入的線程處理時間太久,可以在join()時指定時間,如join(10000),表示加入的線程最多只能處理10秒。
線程完成run()方法后,就進入dead狀態。這時不能再次調用start()方法。
Thread類上的stop()方法是過時方法。
如果要停止線程,最好自行操作,讓線程跑完應有的流程,而不是調用stop()方法
... private boolean isContinue=true; public void stop(){ this.isContinue=false; } public void run(){ while(isContinue){ ... } } ...synchronized
public synchronized void add(Object o){...}
每個對象都有一個內部鎖定。被標示為synchronized的區塊會被監控,任何線程要執行synchronized區塊都必須先獲得指定的對象鎖定。
如果線程A已獲得對象鎖定開始執行sychronized區塊,線程B也想執行synchronized區塊,線程B會因為無法獲得對象鎖定而進入等待對象鎖定狀態,直到線程A釋放鎖定(如執行完synchronized區塊)。
在方法上標示sychronized,則執行方法必須獲得該實例的指定。
實際上等待對象鎖定時,也會進入線程的blocked狀態
synchronized不僅可以聲明在方法上,也可以描述句方式使用
public void add(Object o){ synchronized(this){ ... } }
Listlist=new ArrayList (); synchronized(list){ ... list.add("TheViper");//讓ArrayList線程安全 }
這種方式不用鎖定整個方法,只鎖定會發生競爭狀況的區塊。獲得鎖定的線程執行完這個區塊后,會立即釋放鎖定,其他線程就有機會再競爭這個鎖定.
相比于將整個方法聲明為synchronized,這種方式更有效率。
可以提供不同的對象作為鎖定的來源
private Object lock1=new Object(); private Object lock2=new Object(); public void method1(){ synchronized(lock1){ data1++; ... } } public void method2(){ synchronized(lock2){ data2++; ... } }
在兩個synchronized區塊里,沒有公共的數據,方法。當有一個線程執行method1()而另一個線程執行method2()時,并不會引起共享存取問題,而且一個線程不會因為另一個線程獲得鎖定而阻塞.
java的synchronized提供的是可重入同步,也就是線程獲得某對象鎖定后,若執行過程中又要執行synchronized,而這個鎖定對象的來源又和前面的是同一個,則可以直接執行。
public class Resource { synchronized void doSome(){ } public synchronized void deal(Resource res){ res.doSome(); } }
public class TheadDemo extends Thread{ Resource res1; Resource res2; TheadDemo(Resource res1,Resource res2){ this.res1=res1; this.res2=res2; } @Override public void run(){ for(int i=0;i<10;i++) this.res1.deal(this.res2); } public static void main(String[] args){ Resource res1=new Resource(); Resource res2=new Resource(); Thread thread1=new TheadDemo(res1,res2); Thread thread2=new TheadDemo(res2,res1); thread1.start(); thread2.start(); } }
這段代碼可能出現死鎖,也可能不會。
如果出現的話,原因在于
Thread thread1=new TheadDemo(res1,res2)=>this.res1.deal(this.res2)時,thread1獲得res1的鎖定
Thread thread2=new TheadDemo(res2,res1)=>this.res2.deal(this.res1),thread2獲得res2的鎖定
thread1線程res2.doSome(),準備獲得res2的鎖定,但是res2的鎖定被thread2線程占用,于是thread1線程進入blocked狀態
thread2線程res1.doSome(),準備獲得res1的鎖定,但是res1的鎖定被thread1線程占用,于是thread2線程進入blocked狀態
volatilesynchronized所標志區塊的特點:
互斥性:synchronized區塊在一個時間點上只能有一個線程執行它
可見性:線程離開synchronized區塊后,另一個線程面對的是上一個線程改變后的對象狀態
在java中對于可見性的要求,可以使用volatile達到變量范圍。
沒用volatile,synchronized
public class Variable { static int i=0,j=0; static void deal(){ i++; j++; } static void print(){ System.out.println("i="+i+" j="+j); } }
public class VariableTest1 extends Thread{ @Override public void run(){ while(true) Variable.print(); } }
public class VariableTest extends Thread{ @Override public void run(){ while(true) Variable.deal(); } public static void main(String[] args){ Thread t1=new VariableTest(); Thread t2=new VariableTest1(); t1.start(); t2.start(); } }
... i=638864948 j=638864993 i=638866963 j=638867006 i=638868897 j=638868941 i=638870928 j=638870974 ...
可以看到j大于i,甚至可以遠大于i.原因在于為了效率,線程可以快取變量的值。
t2調用Variable.print()從共享內存中取到變量i的值,并存儲在自己的內存空間,如果此時cpu切換線程至t1,并不斷的執行Variable.deal()多次,再切回t2,取得變量j的值,然后和i值一起輸出。
如果在deal()和print()方法上標志synchronized,則t1每次調用deal()方法時,t2都必須等到t1釋放鎖定才能調用print()方法.t2每次調用print()方法也一樣。
這種情況下,輸出的i一定等于j.
在變量上聲明volatile,表示變量是不穩定的,易變的。這樣變量就可以在多線程情況下存取,保證了變量的可見性.換句話說,如果有線程修改了變量的值,另一個線程一定可以看到被修改的變量。
被標示為volatile的變量,不允許線程快取,變量的存取一定是在共享內存中進行。
下面將上面例子中的i,j聲明為volatile.
volatile static int i=0,j=0;
... i=34456445 j=34456448 i=34456620 j=34456623 i=34456789 j=34456791 i=34456959 j=34456961 ...
可以看到沒有出現j遠大于i的情況,都是i略小于j.
事實上,i和j的關系可以是大于,等于,小于三種之中的任一種。
i大于j
i已經改變,但是j仍然是上一次操作的j.
i小于j
第一次輸出時,保存i,然后j++,第二次輸出的是沒有修改過的i和修改過的j.
i等于j
輸出的都是共享內存中修改過的值.
由此可見,volatile保證的是單一變量可見性,線程對變量的存取一定是在共享內存中進行,不會在自己的內存空間中快取變量。線程對共享內存中變量的存取,另一個線程一定看得到。
等待與通知wait(),notify(),notifyAll()是Object類中的方法,可以通過這三個方法控制線程釋放對象的鎖定,或者通知線程參與鎖定的競爭.
線程進入synchronized區塊前,要先獲得指定對象的鎖定。而在執行synchronized區塊代碼時,若調用鎖定對象的wait()方法,線程會釋放對象鎖定,并進入對象等待集合,其他線程這時可以競爭對象鎖定,取得鎖定的線程可以執行synchronized區塊代碼。
處于等待集合中的線程不會參與競爭cpu.wait()方法可以指定等待時間,時間到之后,線程會參與競爭cpu.如果指定時間為0或沒指定,則線程會一直等待,直到被中斷(interrupt)或通知(notify)可以參與競爭cpu.
鎖定的對象調用notify()方法,會對象等待集合中隨機通知一個線程參與競爭cpu.
如果調用的是notifyAll()方法,則等待集合中的所有線程都會參與競爭cpu.
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64706.html
摘要:然而,這兩個方法都只是讀取對象狀態,如果只是讀取操作,就可以允許線程并行,這樣讀取效率將會提高。分配線程執行子任務執行子任務獲得子任務進行完成的結果 Lock Lock接口主要操作類是ReentrantLock,可以起到synchronized的作用,另外也提供額外的功能。用Lock重寫上一篇中的死鎖例子 import java.util.concurrent.locks.Lock; ...
摘要:一讓廣播明星黯然失色要建立頁面,需要創建用超文本標記語言,編寫的文件,把它們放在一個服務器上二服務器能做什么服務器在互聯網上有一份全天候的工作。一、Web讓廣播明星黯然失色 要建立Web頁面,需要創建用超文本標記語言(HyperText Markup Language,HTML)編寫的文件,把它們放在一個Web服務器上二、Web服務器能做什么? Web服務器在互聯網上有一份全天候的工...
摘要:包括元素的高度上下內邊距上下邊框值,如果元素的的值為那么該值為。該值為元素的包含元素。最后,所有這些偏移量都是只讀的,而且每次訪問他們都需要重新計算。為了避免重復計算,可以將計算的值保存起來,以提高性能。 offsetHeight 包括元素的高度、上下內邊距、上下邊框值,如果元素的style.display的值為none,那么該值為0。offsetWidth 包括元素的寬度、左...
摘要:如果需要收集參數化類型對象,只有使用警告這節討論,向參數可變的方法傳遞一個泛型類型的實例。異常不能拋出或捕獲泛型類的實例實際上,泛型類擴展也是不合法的。 Object:所有類的超類 java中每個類都是由它擴展而來,但是并不需要這樣寫:class Employee extends Object.如果沒有明確指出超類,Object類就被認為是這個的超類。可以使用Object類型的變量引用...
摘要:關鍵字作用調用超類方法調用超類構造器關鍵字作用引用隱式參數如調用該類的其他構造器在覆蓋一個方法時,子類方法可見性不能低于超類方法阻止繼承類和方法目的確保它們不會在子類中改變語義。但是如果將一個類聲明為后面可以改變類變量的值了。 數據類型 整型 int 存儲要求:4byte 取值范圍:-2147483648 -- 2147483647(超過20億) short 存儲要求:2byte 取...
閱讀 2592·2023-04-25 22:09
閱讀 2837·2021-10-14 09:47
閱讀 1889·2021-10-11 11:10
閱讀 2677·2021-10-09 09:44
閱讀 3372·2021-09-22 14:57
閱讀 2493·2019-08-30 15:56
閱讀 1615·2019-08-30 15:55
閱讀 775·2019-08-30 14:13