學習筆記之線程

来源:https://www.cnblogs.com/Hz-z/archive/2020/05/18/12913604.html
-Advertisement-
Play Games

多線程 一、實現多線程 進程 是正在運行的程式 是系統進行資源分配和調用的獨立單位 每一個進程都有它自己的記憶體空間和系統資源 線程 是進程中的單個順序控制流,是一條執行路徑 單線程:一個進程如果只有一條執行路徑,則稱為單線程程式 多線程:一個進程如果有多條執行路徑,則稱為多線程程式 實現多線程方式: ...


多線程

一、實現多線程

進程

是正在運行的程式

  • 是系統進行資源分配和調用的獨立單位
  • 每一個進程都有它自己的記憶體空間和系統資源
線程

是進程中的單個順序控制流,是一條執行路徑

  • 單線程:一個進程如果只有一條執行路徑,則稱為單線程程式
  • 多線程:一個進程如果有多條執行路徑,則稱為多線程程式
實現多線程方式:
  1. 繼承Thread類
    • 定義一個類MyThread繼承Thread類
    • 在MyThread類中重寫run()方法
    • 創建MyThread類的對象
    • 啟動線程

註:run()用來封裝線程執行的代碼。
run():封裝線程執行的代碼,直接調用,相當於普通方法的調用。
strat():啟動線程;然後有JVM調用此線程的run()方法。

Thread類中的方法:設置和獲取線程名稱

  • void setName(String name):將此線程的名稱更改為參數name
  • 也可以通過構造方法來設置
  • String getName():返回此線程的名稱
  • public static Thread currentThread():返回對當前正在執行的線程對象的引用
public class MyThread extends Thread {
    MyThread(){}
    MyThread(String name){
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++){
            System.out.println(getName()+","+i);
        }
    }
}


public class MyThreadDemo {
    public static void main(String[] args) {

        //通過set方法設置線程名稱

//        MyThread mt = new MyThread();
//        MyThread mt2 = new MyThread();
//
//        mt.setName("A:");
//        mt2.setName("B:");
////        mt.run();//直接調用
////        mt2.run();
//        mt.start();
//        mt2.start();//啟動線程,JVM調用run方法

        //通過構造方法設置:
        MyThread mt = new MyThread("A:");
        MyThread mt2 = new MyThread("B:");
        mt.start();
        mt2.start();

        //static Thread currenThread()返回當前正在執行的線程對象的引用
        System.out.println(Thread.currentThread().getName());    // main
    }
}

線程調度

兩種線程調度模型:

  • 分時調度模型:所有線程輪流使用CPU的使用權,平均分配每個線程占用CPU的時間片。
  • 搶占式調度模型:優先讓優先順序高的線程使用CPU。如果線程的優先順序相同,那麼會隨機選擇一個,優先順序高的線程獲取的CPU時間片相對多一些。(java)。

Thread類中設置和獲取線程優先順序的方法

  • public final int getPriority():返回此線程的優先順序。
  • public final void setPriority(int newPriority):更改此線程的優先順序(線程優先順序的範圍是1-10,預設為5)。

線程優先順序高僅僅表示線程獲取CPU時間片的幾率高,但是要在次數比較多,或者多次運行的時候才能看到你想要的效果。

public class ThreadPriority extends Thread{
    ThreadPriority(){}
    ThreadPriority(String name){
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++){
            System.out.println(getName()+":"+i);
        }
    }
}


public class Demo {
    public static void main(String[] args) {
        ThreadPriority tp = new ThreadPriority("A:");
        ThreadPriority tp2 = new ThreadPriority("B:");
        ThreadPriority tp3 = new ThreadPriority("C:");

        System.out.println(Thread.MIN_PRIORITY);//1
        System.out.println(Thread.NORM_PRIORITY);//5
        System.out.println(Thread.MAX_PRIORITY); // 10
        System.out.println("--------------------------------");
        System.out.println(tp.getPriority());//5
        System.out.println(tp2.getPriority());//5
        System.out.println(tp3.getPriority());//5

        System.out.println("-------------------------");
        //設置優先順序
        //public final void setPriority(int newPriority):更改此線程的優先順序

        tp.setPriority(5);
        tp2.setPriority(10);
        tp3.setPriority(1);

        System.out.println(tp.getPriority());//5
        System.out.println(tp2.getPriority());//10
        System.out.println(tp3.getPriority());//1

        System.out.println("-------------------");
        tp.start();
        tp2.start();
        tp3.start();
    }
}
線程式控制制

方法:

  • static void sleep(long millis):使當前正在執行的線程停留(暫停執行)指定的毫秒數
  • void join():等待這個線程死亡
  • void setDaemon(boolean on)將此線程標記為守護線程,當運行的線程是守護線程時,JAVA虛擬機將退出
sleep
public class Sleep extends Thread{
    Sleep(){}
    Sleep(String name){
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++){
            System.out.println(getName()+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


public class SleepDemo {
    public static void main(String[] args) {
        Sleep s = new Sleep("A");
        Sleep s2 = new Sleep("B");
        Sleep s3 = new Sleep("C");

        s.start();
        s2.start();
        s3.start();
    }
}
join
public class JoinDemo {
    public static void main(String[] args) {

        //雍正,胤禩是康熙的兒子 。雍正、胤禩要繼承皇位得康熙退位
//        void join():等待這個線程死亡-->康熙
        Join j = new Join("康熙");
        Join j2 = new Join("雍正");
        Join j3 = new Join("胤禩");

        j.start();
        try {
            j.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        j2.start();
        j3.start();
    }
}


public class Join extends Thread{
    Join(){}
    Join(String name){
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++){
            System.out.println(getName()+i);
        }
    }
}
setDaemon
public class SetDaemon extends Thread{
    public SetDaemon() {
    }

    public SetDaemon(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++){
            System.out.println(getName()+i);
        }
    }
}


public class SetDaemonDemo {
    public static void main(String[] args) {

        //結拜兄弟 同年同月同日死
        SetDaemon sd2 = new SetDaemon("虛竹");
        SetDaemon sd3 = new SetDaemon("段譽");

        //設置為主線程
        Thread.currentThread().setName("喬峰");
        //設置為守護線程
        sd2.setDaemon(true);
        sd3.setDaemon(true);

        sd2.start();
        sd3.start();

        for (int i = 0; i < 10; i++){
            System.out.println(Thread.currentThread().getName()+i);
        }

    }
}

線程生命周期

實現Runnable介面實現多線程
  • 定義一個類MyRunnable實現Runnable介面
  • 實現MyRunnable類中的run()方法
  • 創建MyRunnable類的對象
  • 創建Thread類的對象,把MyRunnable對象作為構造方法的參數
  • 啟動線程

多線程的兩種方式:

  1. 繼承Thread類
  2. 實現Runnable介面

實現Runnable介面:避免了Java單繼承的局限性,適合多個相同程式的代碼去處理同一個資源的情況,把線程和程式的代碼、數據有效分離,較好的體現了面向對象的設計思路

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}


public class MyRunnableDemo {
    public static void main(String[] args) {
        //創建MyRunnable類的對象
        MyRunnable mr = new MyRunnable();

        //創建Thread類的對象,把MyRunnable對象作為構造方法的參數
        Thread t1 = new Thread(mr,"A");
        Thread t2 = new Thread(mr,"B");

        //啟動線程
        t1.start();
        t2.start();

    }
}

線程同步

賣票

public class SellTicket implements Runnable {
    private int Ticket = 100;

    @Override
    public void run() {
        while (true) {
            if (Ticket > 0) {
                try {
                    Thread.sleep(100); //模擬賣票間隔時間
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "售賣第" + Ticket + "張票");
                Ticket--;

            }
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        SellTicket st = new SellTicket();

        Thread t = new Thread(st, "一號視窗:");
        Thread t2 = new Thread(st, "二號視窗");
        Thread t3 = new Thread(st, "三號視窗");

        t.start();
        t2.start();
        t3.start();
    }
}

因為線程執行的隨機性

  • 出現重票
  • 出現負數票

解決

判斷多線程程式是否會有數據安全問題的標準

  • 是否是多線程環境
  • 是否有共用數據
  • 是否有多條語句操作共用數據
解決思路
  • 破壞安全環境
    實現:
  • 將多條語句操作共用數據的代碼鎖起來,讓任意時刻只能有一個線程執行(同步代碼塊)
  • 格式:
synchronized(任意對象){
    多條語句操作共用數據的代碼
}
public class SellTicket implements Runnable {
    private int Ticket = 100;
    private Object obj = new Object();

    @Override
    public void run() {
        synchronized (obj) {
            while (true){
                if (Ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "視窗正在售賣第" + Ticket + "張票");
                    Ticket--;
                }
            }
        }
    }
}

public class SellTicketDemo {
    public static void main(String[] args) {
        SellTicket st = new SellTicket();

        Thread t = new Thread(st, "一號");
        Thread t2 = new Thread(st, "二號");
        Thread t3 = new Thread(st, "三號");

        t.start();
        t2.start();
        t3.start();
    }
}

優缺點:

  • 解決了多線程的安全問題
  • 當線程很多時,因為每個線程都會去判斷同步上的鎖,這很耗費資源,降低了程式的運行效率
同步方法

同步方法:將synchronized關鍵字加到方法上

  • 格式:
    修飾符synchronized返回值類型方法名(參數)
    同步方法的鎖對象:
  • this
public class SellTicket implements Runnable {
    private int Ticket = 100;
    private int x = 0;

    @Override
    public void run() {
        while (true) {
            synchronized (this) {

                if (Ticket > 100) {
                    if (x % 2 == 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "視窗正在售賣第" + Ticket + "張票");
                        Ticket--;
                    } else {
                        sellTicket();
                    }
                    x++;
                }
            }

        }
    }

    private synchronized void sellTicket() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "視窗正在售賣第" + Ticket + "張票");
        Ticket--;
    }
}

同步靜態方法:就是把synchonized關鍵字加到靜態方法上

  • 格式:
    • 修飾符 static synchronized 返回值類型 方法名(方法參數){}
      同步靜態方法的鎖對象
  • 類名+class

線程安全的類

  1. StringBuffer
    • 如果要操作少量的數據用 = String
    • 單線程操作字元串緩衝區 下操作大量數據 = StringBuilder
    • 多線程操作字元串緩衝區 下操作大量數據 = StringBuffer
  2. Vector
    • Vector與ArrayList一樣,也是通過數組實現的,不同的是它支持線程的同步,即某一時刻只有一個線程能夠寫Vector,避免多線程同時寫而引起的不一致性,但實現同步需要很高的花費,因此,訪問它比訪問ArrayList慢。
  3. Hashtable
    Collections類中的synchronizedList(List List)可以返回線程安全的列表、synchronizedMap(Map<K,V> m)可以返回線程安全的映射、......
Lock鎖(JDK5之後)
  1. Lock實現提供比使用synchronized方法和語句獲得更多廣泛的鎖定操作
  • Lock中提供了獲得鎖和釋放鎖的方法
    • void lock():獲得鎖
    • void unlock():釋放鎖
  1. Lock是介面不能直接實例化,這裡採用它實現的類ReentrantLock來實例化
  • ReentrantLock的構造方法
    • ReentrantLock():創建一個ReentrantLock的實例
mport java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicket implements Runnable{
    private int Ticket = 100;
    private Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            try {
                lock.lock();
                if (Ticket > 0){
                    System.out.println(Thread.currentThread().getName()+"視窗正在售賣第"+Ticket+"張票");
                    Ticket--;
                }
            }finally { //用finally來保證必定釋放鎖
                lock.unlock();
            }
        }
    }
}

public class SellTicketDemo {
    public static void main(String[] args) {
        SellTicket st = new SellTicket();

        Thread t = new Thread(st, "一號");
        Thread t2 = new Thread(st, "二號");
        Thread t3 = new Thread(st, "三號");

        t.start();
        t2.start();
        t3.start();
    }
}

生產者消費者

  1. 生產者消費者問題,實際上主要包括了兩類線程:
  • 一類是生產者線程用於生產數據
  • 一類是消費者線程用於消費數據
  1. 為瞭解耦生產者和消費者的關係,通常會採用共用數據區域,就像是一個倉庫
  • 生產者生產數據之後直接放置在共用數據中,並不需要關心消費者的行為
  • 消費者只需要從共用數據區中去獲取數據,並不需要關心生產者的行為

生產者----->共用數據區域<-------消費者

  1. 為體現生產和消費過程的等待和喚醒,java就提供了幾個方法供我們使用,這幾個方法在Object類中Object類的等待和喚醒方法:
  • void wait()導致當前線程等待,知道另一個線程調用該對象的notify()方法或notifyAll()方法
  • void notify()喚醒正在等待對象監視器的單個線程
  • void notifyAll()喚醒正在等待對象監視器的所有線程
/*
生產者消費者案例:
奶箱(Box)定義一個成員變數,表示第x瓶奶,提供存儲牛奶和獲取牛奶的操作
生產者(Producer):實現Runnable介面,實現run()方法,調用存儲牛奶的操作
消費者(Customer):實現Runnable介面,實現run()方法,調用獲取牛奶操作‘
測試類(Demo):裡面有main方法,main方法中的代碼步驟如下:
    1.創建奶箱對象,這是共用數據區域
    2.創建生產者對象,把奶箱對象作為構造方法參數傳遞,因為在這個類中要調用存儲牛奶操作
    3.創建消費者對象,把奶箱對象作為構造方法參數傳遞,因為在這個類中要調用存儲牛奶操作
    4.創建兩個線程對象,分別把生產者對象和消費者對象作為構造方法參數傳遞
    5.啟動線程
 */

 public class Box {
    private int milk;
    private boolean x = false;//牛奶箱的狀態,沒有牛奶

    public synchronized void put(int mike){
        //如果有奶等待消費
        if (x){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.milk = mike;
        System.out.println("員工放入"+milk+"瓶奶");
        x = true;
        notify();
    }

    public synchronized void get(){
        //如果沒有牛奶等待生產
        if (!x){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("用戶拿到"+milk+"瓶奶");
        x = false;
        notify();
    }
}

public class Producer implements Runnable{
    Box b;
    public Producer(Box b) {
        this.b = b;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++){
            b.put(i+1);
        }
    }
}

public class Customer implements Runnable{
    Box b;
    public Customer(Box b) {
        this.b = b;
    }

    @Override
    public void run() {
        while (true){
            b.get();
        }
    }
}


public class Demo {
    public static void main(String[] args) {
        Box b = new Box();
        Producer p = new Producer(b);
        Customer c = new Customer(b);

        Thread t = new Thread(p);
        Thread t2 = new Thread(c);

        t.start();
        t2.start();
    }
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、寫在開頭 無聊寫寫。最近學習做python GUI, 感覺比網頁落後好多。我只是為了完成老師佈置的任務, 做一個配合ZBar掃描條形碼的小程式, 不打算過多深究二維碼什麼的。由於pyqt5貌似不是很火爆, 沒多少成系統的教程。我能找到的就是 "http://code.py40.com/pyqt5 ...
  • 一、IO與Properties的聯合應用 1.Properties解析(可以解析unicode碼) package com.bjpowernode.java_learning; import java.util.*; import java.io.*; public class D116_1_Pro ...
  • 時不時地我們需要導出一些數據用作備份、查看報表等,如果用 導出會非常慢。而用 ,則速度非常快。 準備 執行文件 : sql set colsep , set feedback off set heading off set newp none set pagesize 0 set linesize ...
  • 導入配置 如何優雅的導入scrapy中settings.py的配置參數呢?總不能用 吧,或者 吧。這看起來一點逼格都沒有。 scrapy提供了導入設置的方法:from_crawler 接著,只要在__init__接收這些參數就可以了。 而在一些官方的組件的源碼中會這樣使用,不過這看起來有點多此一舉 ...
  • "這篇博客" 說了怎麼去hook微信來接收好友消息和發送消息,現在就來實現一下,寫了個成品軟體 軟體下載地址:https://www.lanzous.com/ib4g30j 界面很簡單,如圖:(需要註意的是軟體只匹配微信版本2.8.0.121) 主要也就兩個功能。 1、自動聊天:使用騰訊AI開放平臺 ...
  • 歡迎關註我的公眾號“老餘筆記”,也可以訪問我的個人博客www.yuxiaoshao.cn 有需要的可以qq交流學習1316677086 或者加入我的群里交流:901648700 一起分享資源,交流學習 數據類型 數據類型就是用來聲明不同類型的變數或函數的一個廣泛的系統。變數的類型決定了變數存儲在記憶體 ...
  • 本文主要分析 中 的載入,對於其解析我們在後面的文章中專門分析。 是屬於 模塊的,它是對 spring bean 的統一抽象描述定義介面,我們知道在spring中定義bean的方式有很多種,如XML、註解以及自定義標簽,同事Bean的類型也有很多種,如常見的工廠Bean、自定義對象、Advisor等 ...
  • ♩♪♫♬點進來你就知道什麼回事,每一次遇見都是你的幸運♩♪♫♬ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...