線程基礎的一些理解(二)

來源:互聯網
上載者:User

標籤:log   except   靜態   java代碼   延時   hdr   tom   通訊   記憶體回收   

一、線程的調度與控制

  通常電腦只有一個CPU,CPU在某一個時刻只能執行一條命令,線程只有得到CPU時間片,也就是使用權,才可以執行命令。在單核CPU的機器上,線程並不是並行啟動並執行。java虛擬機器主要負責線程調度,取得CPU的使用權,目前有兩種調度模型:分時調度模型和搶佔式調度模型,Java使用搶佔式調度模型。

  分時調度模型:所有線程輪流使用CPU的使用權,平均分配每個線程佔用CPU的時間片

  搶佔式調度模型:優先順序高的線程擷取CPU時間片相對會多一些,如果線程的優先順序相同,那麼會隨機選用一個 。

  下面我們將通過代碼來瞭解如何在java代碼中使用線程,一般我們在工作中是不會去寫多線程的代碼,但如果你是寫伺服器端代碼,如在IBM公司或者寫機器之間的通訊,自己寫一個伺服器端的代碼,那我們就必須考慮多線程了,正常我們用的伺服器像tomcat等已經底層實現多線程了。

  1、關於線程的一些基本方法

    (1)擷取當前線程對象  Thread.currentThread();

    (2)給線程起名  t.setName("t1");

    (3)擷取線程的名字  t.getName();

public class _04線程調度 {    public static void main(String[] args){        //如何擷取當前線程對象?        //t儲存的記憶體位址指向的線程是“主線程對象”        Thread t=Thread.currentThread();        //擷取線程的名字        System.out.println(t.getName());        Thread t1=new Thread(new Processor03());        //給線程起名        t1.setName("t1");        t1.start();                Thread t2=new Thread(new Processor03());        //給線程起名        t2.setName("t2");        t2.start();    }}class Processor03 implements Runnable{    public void run(){        //t儲存的記憶體位址指向的線程是“t1線程對象”        Thread t=Thread.currentThread();        System.out.println(t.getName());    }}

  2、線程的優先順序

  線程優先順序高的線程擷取的cpu時間片相對多一些,也就是獲得執行的機會會大一些,優先順序 1-10

  線程的優先順序主要分三種:MAX_PRIORITY(最進階)(10);MIN_PRIORITY(最低級)(1);NOM_PRIORITY(標準)預設(5)

  擷取以及設定線程的優先順序方法為Thread.getPriority()和Thread.setPriority(),

  從運行結果中可以看到t1線程和t2線程搶奪cpu時間片,由於設定了t1的優先順序高一些,所以t1搶奪的時間片會相對多一些,所以執行的會多一些

  

  3、Thrad.sleep(ms),線程休眠的方法,關於線程休眠有如下幾點注意:

  1.Thread.sleep;

  2.sleep方法是一個靜態方法

  3.該方法的作用,阻塞當前線程(回顧一中線程的執行流),騰出CPU讓給其他線程

    

  4.此方法是靜態方法,所以"引用."和"類名."是一樣的

  下面的代碼中,t.sleep等同於Thread.sleep,故阻塞的並不是t線程,而是阻塞的主線程。

public class _07面試題 {    public static void main(String[] args) throws Exception{        Thread t=new Processor07();        t.setName("t");        t.start();        //等同於Threadsleep        //Thread.sleep()是靜態方法,所以t.sleep()等同於Thread.sleep()        //所以t線程是不會阻塞的,阻塞的是主線程        t.sleep(5000);    }}class Processor07 extends Thread{    public void run(){        for(int i=0;i<3000;i++){            System.out.println(Thread.currentThread().getName()+"---->"+i);        }    }}

  5.中斷休眠的方法

  (1)使用異常機制 t.interrupt(),此interrupt會觸發Interrupted異常可以從休眠中退出。

    

   (2)正常終止,正確的停止一個線程,在外面增加一個表示位

    

二、線程的同步(加鎖)   

  1、非同步編程模型與同步編程模型。
  非同步編程模型:t1線程執行t1的,t2線程執行t2的。兩個線程誰也不等誰
  同步編程模型:t1線程和t2線程執行,當t1線程必須等t2線程執行結束之後,t1線程才能執行,這是同步編程模型
  2、什麼時候同步呢?為什麼要引入線程同步呢?
  1.為了資料安全。暫時就不考慮效率了,儘管應用程式的使用率降低,但是為了保證資料是安全的必須加入同步機制,線程是同步的,所以程式就變成了單線程,就像兩條直線

原來是平行,現在要接在一起變成一根線。
  比如現實生活中,我和你共用一個銀行賬戶,此時這個賬戶裡有5萬塊錢,今天我從這個賬戶裡取了兩萬塊錢,剛取出來,在賬戶還沒有減掉2萬塊錢的時候(此時賬戶裡還是5萬,但我已經取了2萬),
你也來訪問這個賬戶取了3萬塊,這時你返回給賬戶是5萬減3萬,賬戶還有2萬,這是取錢的反饋資訊也返回,賬戶還剩3萬。最後結果是,賬戶原本有5萬,我取了2萬,你取了3萬,賬戶裡還有3萬,這

樣就出現的賬戶安全問題,下面我們會用這個例子來做說明。

  2.什麼條件下要使用線程同步?
  (1):必須是多線程環境
  (2):多線程環境共用同一個資料
  (3):共用的資料涉及到修改操作

    以下程式示範取款例子,首先我們看一下不使用線程同步機制,多線程同時對同一個賬戶進行取款操作,會出現什麼問題?

    首先我們建立一個Account類類比賬戶,裡面有賬戶名稱以及賬戶金額,裡面提供一個withdraw()的提款方法,之後用建立Processer()類實現Runnable介面實現多線程

在構造器中加入賬戶類,run()方法中使用提款方法,在主方法中建立一個新賬戶,建立多線程,我們為了能夠實現會出現安全問題,在withdraw()方法中加了一個1s的sleep延時,

這樣當t1線程,或者t2線程執行時,一個線程執行後,延時1s內,另一個線程會運行這個方法,另一個線程取到的賬戶餘額就不是第一個線程剩下的餘額。而是最開始的餘額,我

們看到最後的結果是t1去了1000 t2取了1000 但是還剩4000元

public class _00線程的同步加鎖 {    public static void main(String[] args) throws Exception{        //建立一個公用賬戶        Account act=new Account("actno--01",5000.0);        //建立線程對同一個賬戶取款        Porcessor p=new Porcessor(act);        Thread t1=new Thread(p);        Thread t2=new Thread(p);                t1.start();        //t1.sleep(2000);        t2.start();    }}//取款線程class Porcessor implements Runnable{    //賬戶    Account act;    //Constructor    Porcessor(Account act){        this.act=act;    }    public void run(){        act.withdraw(1000.0);        System.out.println("取款1000成功,餘額:"+act.getBalance());    }}//賬戶class Account{    private String actno;    private double balance;    public Account(){};    public Account(String actno,double balance){        this.actno=actno;        this.balance=balance;    }    //setter and getter方法    public String getActno(){return this.actno;}    public void setActno(String actno){this.actno=actno;}    public double getBalance(){return this.balance;}    public void setBalance(double balance){this.balance=balance;}    //對外提供一個取款的方法    public void withdraw(double money){//對當前賬戶進行取款        double before=this.balance;        double after=before-money;        //延遲        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        //更新        this.setBalance(after);    }}

運行結果:

取款1000成功,餘額:4000.0取款1000成功,餘額:4000.0

  3.使用線程同步 synchronized關鍵字

  同步原理:

  原理:t1線程和t2線程,t1線程執行到withdraw()方法裡,遇到了synchronized關鍵字,就會去找this對象鎖(本質是每個對象上都有一個0和1的標誌,我們形象的稱它為鎖),如果找到this對象鎖則進入同步語句塊中執行程式,當同步語句塊中的代碼結束之後,t1線程歸還this的對象鎖。
  在本程式中,t1線程執行同步語句塊的過程中,如果t2也過來執行以下代碼,也遇到了synchronized關鍵字,也會去找this的對象鎖,但是該對象鎖被t1持有,只能在這等待this對象鎖歸還才能執行t2線程,

也就是說t1線程在sleep()1s的時候,t2線程也執行到這裡,發現t1還持有鎖,只能等t1線程把賬戶中的錢都減掉才可以執行t2線程的代碼。我們發現啟動並執行結果是正確的。

  但我們在啟動並執行過程中會發現,啟動並執行時間變長了。因為t2線程會等待。

class Account01{    private String actno;    private double balance;        public Account01(){};    public Account01(String actno,double balance){        this.actno=actno;        this.balance=balance;    }    //setter and getter方法    public String getActno(){        return this.actno;    }    public void setActno(String actno){        this.actno=actno;    }    public double getBalance(){        return this.balance;    }    public void setBalance(double balance){        this.balance=balance;    }    //對外提供一個取款的方法    //public void synchronized withdraw(double money){    //} synchronized添加到成員方法上,線程拿走的也是this對象鎖    public void withdraw(double money){//對當前賬戶進行取款        //把需要同步的代碼,放到同步語句塊中         synchronized(this){            double before=this.balance;            double after=before-money;            //延遲            try{Thread.sleep(1000);}catch(Exception e){}            //更新            this.setBalance(after);        }    }}

運行結果:

取款1000成功,餘額:4000.0取款1000成功,餘額:3000.0

  4.使用setDaemon()方法,添加守護線程。

  從線程類型上分可以分為,使用者線程(以上講的都是使用者線程),另一個是守護線程。守護線程是這樣的,所有的使用者線程結束生命週期,守護線程才會結束生命週期
  只要有一個使用者線程存在,那麼守護線程就不會結束,例如java中著名的記憶體回收行程就是一個守護線程,只有應用程式中所有的線程結束,它才會結束,

守護線程一般都是無限執行的。我們可以使用setDaemon()方法將一個線程變為守護線程

  下面的程式如果沒有setDaemon()方法,那麼t1線程會一下運行下去,但是加了setDaemon()方法,t1變成守護線程,當主線程運行完後,t1線程就會結束。

public class _00守護線程 {    public static void main(String[] args) throws Exception{        Thread t1=new Processor();        t1.setName("守護線程");        //將t1這個使用者線程修改成守護線程        t1.setDaemon(true);        t1.start();        for(int i=0;i<10;i++){            System.out.println(Thread.currentThread().getName()+"-->"+i);            Thread.sleep(1000);        }    }}class Processor extends Thread{        public void run(){        int i=0;        while(true){            i++;            System.out.println(Thread.currentThread().getName()+"-->"+i);            try {                Thread.sleep(500);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

 

線程基礎的一些理解(二)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.