1. 程式人生 > 其它 >JAVA基礎 多執行緒

JAVA基礎 多執行緒

技術標籤:# 多執行緒JAVA基礎第一階段java多執行緒

一、多執行緒:

1.概述:

用多執行緒只有一個目的,那就是更好的利用cpu的資源,提高程式的執行效率,因為所有的多執行緒程式碼都可以用單執行緒來實現。

一、多執行緒:

多執行緒:指的是這個程式(一個程序)執行時產生了不止一個執行緒
並行與併發:
並行:多個cpu例項或者多臺機器同時執行一段處理邏輯,是真正的同時。
併發:通過cpu排程演算法,讓使用者看上去同時執行,實際上從cpu操作層面不是真正的同時。

二、執行緒安全:

經常用來描繪一段程式碼。指在併發的情況之下,該程式碼經過多執行緒使用,執行緒的排程順序不影響任何結果。這個時候使用多執行緒,我們只需要關注系統的記憶體,cpu是不是夠用即可。反過來,執行緒不安全就意味著執行緒的排程順序會影響最終結果,如不加事務的轉賬程式碼:

void transferMoney(User from, User to, float amount){
    to.setMoney(to.getBalance() + amount);
    from.setMoney(from.getBalance() - amount);
}

同步:

Java中的同步指的是通過人為的控制和排程,保證共享資源的多執行緒訪問成為執行緒安全,來保證結果的準確。
如上面的程式碼簡單加入@synchronized關鍵字。
在保證結果準確的同時,提高效能,才是優秀的程式。
執行緒安全的優先順序高於效能。

2,程序和執行緒的區別

程序:計算機中特定功能的程式在資料集上的一次執行。

執行緒:執行緒是程序的一個單元。
多執行緒:一個程序中有多個執行緒在同時執行,如迅雷軟體的一次執行就是一個程序,那麼在迅雷中可以同時下載多個電影,這就是多執行緒(每一個下載都是一個執行緒)
JVM是多執行緒的,在我們執行JVM的時候後臺會執行垃圾回收的執行緒,來清理沒有被引用的物件.
程序:
執行緒1、執行緒2、執行緒3 …

登入一個QQ就是一個程序:
與多個人聊天對話,就是多個執行緒。

3.並行和併發的區別

–併發: 是 多個程式 搶佔 CPU的執行權
–並行: 是 多個CPU,對應多個程式,每個CPU執行一個程式,不用搶
–效率: 併發 > 並行

4.模擬多執行緒程式設計方式

–繼承Thread:好處是可以使用父類的所有功能,壞處是單繼承/強耦合

–實現Runnable介面:好處是解耦合,可以多繼承多實現,壞處是??


二、執行緒的實現:

1.執行緒實現的第一種方法:

Thread
建立新執行執行緒有兩種方法。
一種方法是將類宣告為Thread的子類,該子類應重寫Thread類的run方法。
接下來可以分配並啟動該子類的例項。
執行緒啟動的時候 使用執行緒的start方法而不是run!

2.執行緒實現的第二種方法:

	Runnable 介面應該由那些打算通過某一執行緒執行其例項的類來實現。
	類必須定義一個稱為 run 的無引數方法。 
     常用方法
	void run()  

繼承Thread類

–1,概述

執行緒 是程式中的執行執行緒。
Java 虛擬機器允許應用程式併發地執行多個執行執行緒。

–2,建立物件

Thread()
分配新的 Thread 物件。
Thread(Runnable target)
分配新的 Thread 物件。
Thread(Runnable target, String name)
分配新的 Thread 物件。
Thread(String name)
分配新的 Thread 物件。

–3,常用方法

static Thread currentThread()
返回對當前正在執行的執行緒物件的引用。
long getId()
返回該執行緒的識別符號。
String getName()
返回該執行緒的名稱。
void run()
void setName(String name)
改變執行緒名稱,使之與引數 name 相同。
static void sleep(long millis)
void start()
使該執行緒開始執行;Java 虛擬機器呼叫該執行緒的 run 方法。
void stop()
已過時。

三、執行緒的建立方式:

1.方式一 繼承Thread:

繼承Thread類 子類要重寫run方法

public class MyThread extends Thread{
   //給執行緒定個變數,取個名字
    private String name;
    public MyThread(String name){
        this.name = name;
    }
    
        /* 執行緒的執行邏輯體 */
        @Override
        public void run(){
            for (int i = 0; i <= 100; i++) {
                System.out.println(name+"下載了:"+i+"%");
         }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        //建立一個執行緒的物件
        MyThread mt = new MyThread("極品飛車14");
        //啟動一個執行緒
        mt.start();//開啟執行緒

        //建立一個執行緒物件
        MyThread my = new MyThread("GTA5");
        //啟動一個執行緒
        my.start();//開啟執行緒

        //System.out.println("下載結束");
    }
}

方式二 Runnable介面:

Runnable 介面應該由那些打算通過某一執行緒執行其例項的類來實現。
類必須定義一個稱為 run 的無引數方法。

public class ThreadTest {
    public static void main(String[] args) {
        //建立執行緒物件
        //建立2個例項的執行緒
        Thread t = new Thread(new DownLoad("極品飛車14")); //new DownLoad 建立例項
        Thread t1 = new Thread(new DownLoad("GTA5")); //new DownLoad 建立例項

        t.start();
        t1.start();
    }
}
public class DownLoad implements Runnable {
    private String name;
    public DownLoad(String name){
        this.name=name;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(name+"下載了:"+i+"%");
        }
    }
}

執行緒的生命週期

**
1.新建:執行緒被new出來
2.準備就緒:執行緒具有執行的資格,即執行緒呼叫了start(),沒有執行的權利
3.執行:具有執行的資格和具備執行的權利
4.阻塞:沒有執行的資格和執行權利
5.銷燬:執行緒的物件變成垃圾,釋放資源。**

在這裡插入圖片描述


併發

網際網路的專案中存在大量的併發案例,如賣火車票、電商網站。

–範例:

火車站有100票,4個視窗同時買票

–分析:

4個視窗是4個執行緒同時在執行,100張票是4個執行緒的共享資源
採用繼承Thread來實現

併發安全性問題:
4個執行緒同時搶100張票

package demo;
/**
 * 這個是賣票的視窗,我們建立4個物件就是4個視窗
 */
public class SaleTicketThread extends Thread{
    private String name;
    /**
     * 共享的資料 100張票
     */
    static int tickets = 100;
    public SaleTicketThread(String name){
        this.name=name;
    }

    @Override
    public void run(){
        //賣票是死迴圈
        while (true){
           if (tickets > 0){
               System.out.println(name+"賣出的座位是:"+(tickets--)+"號");
           }else{
               break;
           }
            try {
                Thread.sleep(100);//休眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(name+"售票結束,已無票");
    }
}

測試程式碼

package demo;

public class ThreadTest {
    public static void main(String[] args) {
        SaleTicketThread t1 = new SaleTicketThread("同程");
        SaleTicketThread t2 = new SaleTicketThread("飛豬");
        SaleTicketThread t3 = new SaleTicketThread("高鐵管家");
        SaleTicketThread t4 = new SaleTicketThread("支付寶");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

執行緒併發問題的解決方法(synchronized)

synchronized(鎖物件)

語法:
    synchronized(鎖物件){
      //操作共享資源的程式碼
    }
package demo;

/**
 * 這個是賣票的視窗,我們建立4個物件就是4個視窗
 */
public class SaleTicketThread extends Thread{
    private String name;
    /**
     * 共享的資料 100張票
     */
    static int tickets = 100;
    //建立一個鎖物件,這個物件是多個執行緒共享的資料
    static Object obj=new Object();

    public SaleTicketThread(String name){
        this.name=name;
    }

    @Override
    public void run(){
        //賣票是死迴圈
        while (true){
            synchronized (obj){
                if (tickets > 0){
                    System.out.println(name+"賣出的座位是:"+(tickets--)+"號");
                }else{
                    break;
                }
            }
            try {
                Thread.sleep(200);//休眠2秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(name+"售票結束,已無票");
    }
}

測試程式碼

package demo;

public class ThreadTest {
    public static void main(String[] args) {
        SaleTicketThread t1 = new SaleTicketThread("同程");
        SaleTicketThread t2 = new SaleTicketThread("飛豬");
        SaleTicketThread t3 = new SaleTicketThread("高鐵管家");
        SaleTicketThread t4 = new SaleTicketThread("支付寶");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

使用Runnable介面解決併發問題

/**

  • Thread.currentThread().getName() :是一個靜態方法
  • this.getName():是一個例項方法
  • JAVA的方法分為:類方法和例項方法:
  • 區別是類方法中有static修飾的,為靜態方法,是類方法。
  • 無關鍵字修飾的為例項方法
  • 注意:
  • static關鍵字一定要是最先宣告的,在說明符float或void前。
    */
 package demo2;

/**
 * Thread.currentThread().getName() :是一個靜態方法
 * this.getName():是一個例項方法
 * JAVA的方法分為:類方法和例項方法:
 * 區別是類方法中有static修飾的,為靜態方法,是類方法。
 * 無關鍵字修飾的為例項方法
 * 注意:
 *    static關鍵字一定要是最先宣告的,在說明符float或void前。
 */
public class SaleTicket implements RunnableRunnable {
    //多執行緒共享的資料 100張票
     int tickets = 100;
    //建立一個鎖物件,這個物件是多個執行緒共享的資料
     Object obj=new Object();

    @Override
    public void run(){
        //賣票是死迴圈
        //是持續的
        while (true){
            synchronized (obj){
                if (tickets > 0){
                    System.out.println(Thread.currentThread().getName()
                            + "賣出的座位是:"+(tickets--)+"號");
                }else{
                    break;
                }
            }
            try {
                Thread.sleep(200);//休眠2秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName()
                +"售票結束,已無票");
    }
  }

測試程式碼:

package demo2;

public class ThreadTest {
    public static void main(String[] args) {
        //建立一個賣票的物件
        SaleTicket st =new SaleTicket();

        Thread t1 = new Thread(st,"飛豬");
        Thread t2 = new Thread(st,"同程");
        Thread t3 = new Thread(st,"攜程");
        Thread t4 = new Thread(st,"管家");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

synchronized在方法上修飾的特點

如果synchronized放在物件方法上:

package demo2;
public class SaleTicket implements Runnable {
    //多執行緒共享的資料 100張票
    int tickets = 100;
    //建立一個鎖物件,這個物件是多個執行緒共享的資料
    Object obj = new Object();

    @Override
    public void run() {
        //賣票是死迴圈
        //是持續的
        while (true) {
            if (seleTickets()) {
                break;
            }
            try {
                Thread.sleep(200);//休眠2秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName()
                + "售票結束,已無票");
    }
 /**
     *
     * @return 如果一個物件方法上有synchronized的話 那麼鎖的物件就是this
     */
    public synchronized boolean seleTickets() {
        //  synchronized (obj) {
        boolean isFinish = false;
        if (tickets > 0) {
            System.out.println(Thread.currentThread().getName()
                    + "賣出的座位是:" + (tickets--) + "號");
        } else {
            isFinish = true;
        }
        return isFinish;
        // }
    }
}

測試程式碼:

package demo2;

public class ThreadTest {
    public static void main(String[] args) {
        //建立一個賣票的物件
        SaleTicket st =new SaleTicket();

        Thread t1 = new Thread(st,"飛豬");
        Thread t2 = new Thread(st,"同程");
        Thread t3 = new Thread(st,"攜程");
        Thread t4 = new Thread(st,"管家");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

synchronized 如果在類方法上 那麼鎖物件就是類的類物件


package demo;
/**
 * 這個是賣票的視窗,我們建立4個物件就是4個視窗
 */
public class SaleTicketThread extends Thread{
    private String name;
    /**
     * 共享的資料 100張票
     */
    static int tickets = 100;
    //建立一個鎖物件,這個物件是多個執行緒共享的資料
    static Object obj=new Object();

    public SaleTicketThread(String name){
        super(name);//把名字給執行緒
        this.name=name;
    }

    @Override
    public void run(){
        //賣票是死迴圈
        while (true){
            if (saleTickets()){
                break;
            }
            try {
                Thread.sleep(200);//休眠2秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(name+"售票結束,已無票");
    }

    public static synchronized boolean saleTickets(){
         boolean isFinish = false;
            if (tickets > 0){
                System.out.println(Thread.currentThread().getName()
                        +"賣出的座位是:"+(tickets--)+"號");
            }else {
                isFinish = true;
            }
           return isFinish;
    }
}

測試程式碼:

package demo2;

public class ThreadTest {
    public static void main(String[] args) {
        //建立一個賣票的物件
        SaleTicket st =new SaleTicket();

        Thread t1 = new Thread(st,"飛豬");
        Thread t2 = new Thread(st,"同程");
        Thread t3 = new Thread(st,"攜程");
        Thread t4 = new Thread(st,"管家");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}