java進程/線程;堆和棧;多線程


一.進程和線程

進程:在內存中運行的應用程序,一個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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM