一.進程和線程
進程:在內存中運行的應用程序,一個exe是一個進程。
如:ps -exf 可以查看各個應用的進程,其中ppid為父進程;
ps aux | egrep '(cron|syslog)' 找出與 cron 與 syslog 這兩個服務有關的 PID 號碼 ;
kill -9 進程id 可以關閉該進程
線程:進程中的一個執行流程,共享同一個進程的內存。
二.堆與棧
堆內存:存放new出來的對象和數組,分出的內存由jvm的自動垃圾回收器進行管理。
棧內存:定義一個變量,取值等於new出來的對象和數組的首地址,即取一個變量名,實際的值存在堆內存中,地址放在定義的變量中,該變量被稱為數組或對象的引用變量
(堆保存實際數據,為屬性內容,棧保存數值的空間地址,為對象名稱)
例如:int[] a=new int a[10] 為堆內存分配一段連續的內存空間
a[2]=4 引用變量相當於給數組起個名字,以后可以引用
內存分配:
1.類變量(static修飾):在堆中分配,堆中的內存地址存放在棧中,方便高速訪問
2.實例變量:如 a[2]=4 ,作為堆對象的引用變量,引用完,被GC(garbage collection)垃圾回收器列入可回收的名單,但是不馬上釋放堆內存
3.局部變量:一般為for循環內定義,執行時在棧中開辟內存,脫離作用域后,內存釋放,所以for內的變量一般定義在for內,而不是for外,防止占用內存
具體內存分配參照:http://blog.csdn.net/qh_java/article/details/9084091
三.線程與內存
Thread類實例是一個對象,有變量和方法,生死於堆上
每個線程實例有一個調用棧,每創建一個線程,就產生一個調用棧,記錄函數執行順序,開獨立棧是為了並行運行
線程分類:1.用戶進程 (main為主線程,其他線程為用戶進程)
2.守護進程:程序運行時在后台提供一種通用服務的線程,如:垃圾回收線程,內存管理線程,數據庫連接池監控線程
非守護線程結束,則守護線程沒有存在的必要,此時jvm退出,守護線程也關閉!!

四.線程的生命周期
線程生命周期的五種狀態:

(1)新建(new Thread)
當創建Thread類的一個實例(對象)時,此線程進入新建狀態(未被啟動)。
例如:Thread t1=new Thread();
(2)就緒(runnable)
線程已經被啟動,正在等待被分配給CPU時間片,也就是說此時線程正在就緒隊列中排隊等候得到CPU資源。例如:t1.start();
(3)運行(running)
線程獲得CPU資源正在執行任務(run()方法),此時除非此線程自動放棄CPU資源或者有優先級更高的線程進入,線程將一直運行到結束。
當發生如下情況是,線程會從運行狀態變為阻塞狀態:
①、線程調用sleep方法主動放棄所占用的系統資源
②、線程調用一個阻塞式IO方法,在該方法返回之前,該線程被阻塞
③、線程試圖獲得一個同步監視器,但更改同步監視器正被其他線程所持有
④、線程在等待某個通知(notify)
⑤、程序調用了線程的suspend方法將線程掛起。不過該方法容易導致死鎖,所以程序應該盡量避免使用該方法。
(4)死亡(dead)
當線程執行完畢或被其它線程殺死,線程就進入死亡狀態,這時線程不可能再進入就緒狀態等待執行。
自然終止:正常運行run()方法后終止
異常終止:調用stop()方法讓一個線程終止運行
(5)堵塞(blocked)
由於某種原因導致正在運行的線程讓出CPU並暫停自己的執行,即進入堵塞狀態。
正在睡眠:用sleep(long t) 方法可使線程進入睡眠方式。一個睡眠着的線程在指定的時間過去可進入就緒狀態。
正在等待:調用wait()方法。(調用motify()方法回到就緒狀態)
被另一個線程所阻塞:調用suspend()方法。(調用resume()方法恢復)
狀態控制和相關方法見:http://blog.csdn.net/lonelyroamer/article/details/7949969
五.如何創建多線程
多線程實現方式有2種:
(1)繼承Thread類(單繼承),並重寫run方法
(2)實現Runnable接口(當創建的類繼承了其他類時)
ps:為什么要單繼承,多接口?--》防止繼承的多個類中有相同的方法,不易區分,而接口沒有方法體(多為抽象方法),可以多繼承
先來編寫一個單線程:
public class Mythread { @SuppressWarnings("static-access") public static void main(String[] args) { Thread t =Thread.currentThread(); t.setName("單例線程"); System.out.println(t.getName()+" 正在運行"); for(int i=0;i<5;i++){ System.out.println("線程正在休眠:"+i); try { t.sleep(500); } catch (InterruptedException e) { System.out.println("線程出現錯誤了!!"); } } }}
運行結果:
單例線程 正在運行 線程正在休眠:0 線程正在休眠:1 線程正在休眠:2 線程正在休眠:3 線程正在休眠:4
編寫一個多線程,使用第一種方法:
package thread; public class Mythread3 extends Thread { private String name; private int ms; //封裝線程名和時間 /*使用類方法,直接賦值類中的類方法,在該類被加載到內存時,就分配了相應的入口地址。 從而類方法不僅可以被類創建的任何對象調用執行,也可以直接通過類名調用。類方法的入口地址直到程序退出才被取消。 類方法在類的字節碼加載到內存時就分配了入口地址,因此,Java語言允許通過類名直接調用類方法,而實例方法不能通過類名調用。*/ public Mythread3(String name, int ms) { this.name = name; this.ms = ms; } // 每個線程start()后進行run() @Override public void run() { // TODO Auto-generated method stub try { sleep(ms); } catch (InterruptedException e) { // TODO: handle exception System.out.println("線程出錯啦"); } System.out.println(name+"開始休眠"+ms); } public static void main(String[] args) { // TODO Auto-generated method stub Thread t1=new Mythread3("線程1",100); Thread t2=new Mythread3("線程2",300); Thread t3=new Mythread3("線程3",200); t1.start(); t2.start(); t3.start(); } }
運行結果:
線程1開始休眠100
線程3開始休眠200
線程2開始休眠300
使用第二種方法:
package thread; class MyThread extends Thread{ public int x = 0; public void run(){ System.out.println(++x); } } class R implements Runnable{ private int x = 0; public void run(){ System.out.println(++x); } } public class Test{ public static void main(String[] args){ try { for(int i=0;i<10;i++){ Thread t = new MyThread(); //Mythread類繼承thread,使用方法(1),打印十次1 t.start(); } Thread.sleep(10000);//讓上面的線程運行完成 /*R類使用接口runnable,使用方法(2),10個線程對象產生的10個線程運行時打印了1到10。 10個線程稱為同一實例(Runnable實例)的多個線程。*/ R r = new R(); for(int i=0;i<10;i++){ Thread t1 = new Thread(r); t1.start(); } } catch (Exception e) { // TODO: handle exception } } }
運行結果:
六.線程同步
臨界資源:多個線程共享的數據
java對象默認可以被多個線程共用,當用sychronized修飾時,則啟動“互斥鎖”機制,任一時刻只能由一個線程訪問,即使該線程阻塞。
線程同步的方法有兩種:
(1)synchronized(互斥鎖)
(2)wait與notify
可見: http://blog.csdn.net/ff55c/article/details/6748604
