1. 程式人生 > 其它 >執行緒基礎程式碼總結

執行緒基礎程式碼總結

技術標籤:Java基礎知識java

執行緒程式碼總結

1.執行緒建立第一種方式(繼承Thread類)重寫run方法

下劃線代表新的類,可自行建立

public class Demo01Thread {
    public static void main(String[] args) {
        MyThread m = new MyThread();
        m.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("main"+i);
        }
} } ------------------------------ public class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println("run:"+i); } } }

執行緒的sleep方法

public class Demo4 {
    public static void main(String[] args)
{ //模擬秒錶 for (int i = 1; i <= 60; i++) { System.out.println(i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }

2.執行緒建立第二種方式(實現Runnable介面)重寫run方法實現Runnable介面的好處:

實現Runnable介面的好處

  • 1.避免了單繼承的侷限性 實現了Runnable介面 還可以實現其他介面
  • 2.增強了程式的擴充套件性 降低了耦合性
    把設定執行緒任務和開啟新執行緒進行了分離 可以傳遞不同的實現類,就可以執行不同的執行緒任務
public class Demo5 {
    public static void main(String[] args) {
        Runnable run = new MyThread5();
        Thread t = new Thread(run);
        t.start();
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}
--------------------------------------
public class MyThread5 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}

補充:start()和run()的區別

  • run():Thread本身也是實現Runnable介面。Runnable介面只有一個方法就是run方法。啟動執行緒時,會使得run方法在那個獨立執行的執行緒中被呼叫;run方法的內容就是執行緒任務也稱為執行緒體
  • start():start方法就是啟動執行緒,會導致run方法的呼叫。
  • 使用start方法,是真的啟動了執行緒,達到了非同步執行的效果。而使用run方法並沒有真的啟動執行緒,而是由main執行緒去呼叫run方法,還是在main執行緒裡執行。

匿名內部類實現執行緒的建立(函數語言程式設計也可以)

public class Demo6 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println(Thread.currentThread().getName()+"-->"+i);
                }
            }
        });
        t.start();
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}

3.由多執行緒引出的執行緒安全問題:多執行緒訪問共享資料,就會產生經典的賣票問題(出現0、-1或者同時出現100)

解決:解決:保證同一時間只有一個執行緒在操作共享資料。即執行緒同步技術

3.1同步程式碼塊(推薦使用)
public class RunnableImpl implements Runnable{
    //定義一個多個執行緒共享的票源
    private int ticket = 100;

    //建立一個鎖物件  保證其唯一性
    Object o = new Object();
    @Override
    public void run() {
        while (true){
            //建立一個同步程式碼塊
            synchronized (o){
                if(ticket>0) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"-->正在賣第"+ticket+"張票");
                    ticket--;
                }
            }
        }
    }
}
--------------------------------
public class Temo4 {
    public static void main(String[] args) {
        Runnable run = new RunnableImpl();
        Thread t = new Thread(run);
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        t.start();
        t1.start();
        t2.start();
    }
}

過程解釋:
t搶到了CPU執行權,執行run方法,遇到synchronized程式碼塊,此時t檢查synchronized程式碼塊是否有鎖物件,發現有,就會獲取鎖物件,進入同步執行;t2搶到了CPU執行權,執行run方法,遇到synchronized程式碼塊,此時t2檢查synchronized程式碼塊是否有鎖物件,發現沒有,t2進入阻塞狀態,會一直等待t歸還鎖物件,一直到t執行完同步程式碼塊,把鎖物件歸還給同步程式碼塊,此時t2獲得鎖物件,進入同步程式碼塊執行。
同步中的執行緒,沒有執行完畢不會釋放鎖,同步外的執行緒沒有鎖進不去同步

3.2同步方法
public class RunnableImpl implements Runnable{
    //定義一個多個執行緒共享的票源
    private int ticket = 100;

    //建立一個鎖物件  保證其唯一性
    Object o = new Object();
    @Override
    public void run() {
        payTicket();
    }
    /*
    定義一個同步方法
     */
    public synchronized void payTicket() {
        while (true){
            //建立一個同步程式碼塊
            synchronized (o){
                if(ticket>0) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"-->正在賣第"+ticket+"張票");
                    ticket--;
                }
            }
        }
    }
}
----------------------------------
public class Temo5 {
    public static void main(String[] args) {
        Runnable run = new RunnableImpl();
        Thread t = new Thread(run);
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        t.start();
        t1.start();
        t2.start();
    }
}
3.Lock鎖
public class RunnableImpl implements Runnable{
    //1.在成員位置建立一個ReentrantLock物件
    Lock l = new ReentrantLock();
    private int ticket = 100;

    @Override
    public void run() {
        while (true){
            //2.在可能出現安全問題的程式碼前呼叫lock方法獲取lock鎖
            l.lock();
                if(ticket>0) {
                    try {
                        Thread.sleep(10);
                        System.out.println(Thread.currentThread().getName()+"-->正在賣第"+ticket+"張票");
                        ticket--;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        //3.在可能出現安全問題的程式碼後釋放lock方法獲取lock鎖
                        //無論程式是否異常 都釋放鎖
                        l.unlock();
                    }
                }
        }
    }
}
---------------------------------
public class Demo1 {
    public static void main(String[] args) {
        Runnable run = new RunnableImpl();
        Thread t = new Thread(run);
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        t.start();
        t1.start();
        t2.start();
    }
}

4.執行緒通訊

概念:多個執行緒在處理同一資源,但是各自執行緒的任務卻不相同,所以存線上程通訊
wait()和notify()必須由同一個鎖物件呼叫(重點)
notify()和wait()必須在同步程式碼塊或同步函式中使用(重點)

4.1示例 包子問題

Step1:建立一個資源類

public class BaoZi {
    String pi;
    String xian;
   //flag表示是否有包子
    boolean flag = false;
}

Step2:建立包子鋪執行緒類

/*
使用包子物件作為鎖物件
 */
public class BaoZiPu extends Thread{
    private BaoZi bz;
    public BaoZiPu(BaoZi bz) {
        this.bz = bz;
    }
    //執行緒任務 生產包子
    @Override
    public void run() {
        int count = 0;
        while (true) {
            synchronized (bz) {
            if (bz.flag==true) {
                try {
                    //有包子,包子鋪執行緒等待
                    bz.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //被喚醒之後執行,包子鋪生產包子  生產兩種包子 根據count% 2==0的結果
            if(count%2==0) {
                bz.pi = "薄皮";
                bz.xian = "三鮮";
            }else {
                bz.pi = "冰皮";
                bz.xian = "牛肉";
            }
            count++;
            System.out.println("包子鋪正在生產"+bz.pi+bz.xian+"包子");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            bz.flag=true;
            //喚醒吃貨執行緒
            bz.notify();
            System.out.println("包子鋪已經生產"+bz.pi+bz.xian+"包子,可以開始吃了");
        }}

    }
}

Step3:建立吃貨執行緒類

public class ChiHuo extends Thread{
    private BaoZi bz;
    public ChiHuo(BaoZi bz) {
        this.bz = bz;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (bz) {
                if(bz.flag==false){
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //被喚醒之後執行的程式碼
                System.out.println("吃貨正在吃:"+bz.pi+bz.xian+"的包子");
                bz.flag=false;
                bz.notify();
                System.out.println("吃貨已經吃完了:"+bz.pi+bz.xian+"的包子"+"...包子鋪開始生產包子");
            }
        }
    }
}

Step4:測試類

public class Demo {
    public static void main(String[] args) {
        BaoZi bz = new BaoZi();
        //開啟包子鋪執行緒 生產包子
        new BaoZiPu(bz).start();
        //開啟吃貨執行緒,吃包子
        new ChiHuo(bz).start();
    }
}

執行結果截圖:
在這裡插入圖片描述

5.執行緒池

Executor 執行緒池的工廠類 用來生成執行緒池

submit(Runnable task)提交一個Runnable任務用於執行
void shutdown() 關閉銷燬執行緒池的方法
使用步驟
1:使用靜態方法newFixedThreadPool生成一個指定執行緒數量的執行緒池
2:建立一個類,實現Runnable介面,重寫run方法,設定執行緒任務
3:呼叫ExecutorService的方法submit,傳遞執行緒任務,開啟執行緒,執行run方法
4:呼叫ExecutorService的方法shutdown,銷燬執行緒池(不建議執行)

public class Demo1 {
   public static void main(String[] args) {
       //1.
       ExecutorService es = Executors.newFixedThreadPool(2);
       //3.
       es.submit(new RunnableImpl());
       es.submit(new RunnableImpl());
       es.shutdown();
   }
}
-----------
//2.
public class RunnableImpl implements Runnable{
   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName()+"建立了一個新的執行緒執行");
   }
}