一.進程和線程java
進程:在內存中運行的應用程序,一個exe是一個進程。數據庫
如:ps -exf 能夠查看各個應用的進程,其中ppid爲父進程;數組
ps aux | egrep '(cron|syslog)' 找出與 cron 與 syslog 這兩個服務有關的 PID 號碼 ;多線程
kill -9 進程id 能夠關閉該進程jvm
線程:進程中的一個執行流程,共享同一個進程的內存。 ide
二.堆與棧函數
堆內存:存放new出來的對象和數組,分出的內存由jvm的自動垃圾回收器進行管理。this
棧內存:定義一個變量,取值等於new出來的對象和數組的首地址,即取一個變量名,實際的值存在堆內存中,地址放在定義的變量中,該變量被稱爲數組或對象的引用變量spa
(堆保存實際數據,爲屬性內容,棧保存數值的空間地址,爲對象名稱).net
例如: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