多線程複習(Java + Python)

Java多線程

本章內容來自尚學堂Java300集視頻第二季多線程部分總結

  • 程序、進程、線程

程序:指令集 靜態概念

進程:操作系統調度程序 動態概念

線程:在進程內多條執行路徑

一個進程中的線程共享相同的內存單元/內存地址空間->可以訪問相同的變量和對象,而且它們從同一堆中分配對象->通信、數據交換、同步操作

由於線程間的通信是同一地址空間上進行的,所以不需要額外的通信機制

區別 進程 線程
根本區別 作爲資源分配的單位 調度和執行的單位
開銷 每個進程都有獨立的代碼和數據空間(進程上下文),進程間的切換會有較大開銷。 線程可以看成是輕量級進程,同一類線程共享代碼和數據空間,每個線程有獨立的運行棧和程序計數器(PC),線程切換開銷小。
所處環境 在操作系統中能同時運行多個任務(程序) 在同一應用程序中有多個順序流同時執行
分配內存 系統在運行的時候會爲咩哥進程分配不同的內存區域 除了CPU之外,不會爲線程分配內存(線程所使用的資源是它所屬的進程的資源),線程組只能共享資源
包含關係 沒有線程的進程是可以被看作單線程的,如果一個進程內擁有多個線程,則執行過程不是一條線的,而是多條線程共同完成的。 線程是進程的一部分

創建線程

繼承Thread + 重寫run(線程體)

package com.zjn.thread.create;

/**
 * 模擬龜兔賽跑
 * 1.創建多線程,繼承Thhread + 重寫run(線程體)
 * 2.使用線程:創建子類對象 + 調用對象.start()方法 線程啓動
 */
public class Rabbit extends Thread{

    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println("兔子跑了"+i+"步");
        }
    }

}
class Tortoise extends Thread {
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println("烏龜跑了"+i+"步");
        }
    }
}

package com.zjn.thread.create;

public class RabbitApp {
    public static void main(String[] args) {
        //創建子類對象
        Rabbit rab = new Rabbit();
        Tortoise tor = new Tortoise();

        //調用start方法
        rab.start();
        tor.start();
    }
}

控制檯輸出

兔子跑了0步
兔子跑了1步
兔子跑了2步
兔子跑了3步
兔子跑了4步
兔子跑了5步
烏龜跑了0步
兔子跑了6步
...

繼承Thread類方式的缺點:如果我們的類已經從一個類繼承,則無法再繼承Thread類

通過Runnable接口實現多線程

優點:可以同時實現繼承。實現Runnable接口方式要通用一些
使用Runnable 創建線程

  1. 避免單繼承的侷限性

  2. 便於共享資源

  3. 類實現Runnable接口+重寫run() -->真實角色類

  4. 啓動多線程 使用靜態代理

    1) 創建真實角色
    2)創建代理角色
    3) 調用start()方法

package com.zjn.thread.create;
public class Programmer implements Runnable{

    @Override
    public void run() {
        for (int i=0;i<1000;i++){
            System.out.println("一邊敲代碼");
        }
    }
}

package com.zjn.thread.create;

public class ProgrammerApp {
    public static void main(String[] args) {
        Programmer pro = new Programmer();
        Thread proxy = new Thread(pro);
        proxy.start();

        for (int i=0;i<1000;i++){
            System.out.println("一邊聊天");
        }
    }
}

控制檯輸出:

...
一邊聊天
一邊聊天
一邊聊天
一邊聊天
一邊聊天
一邊敲代碼
一邊敲代碼
一邊敲代碼
一邊敲代碼
...

靜態代理設計模式

package com.zjn.thread.create;

/**
 * 靜態代理 設計模式
 * 1.真實角色
 * 2.代理角色:持有真實角色的引用
 * 3.二者實現相同接口
 */
public class StaticProxy {
    public static void main(String[] args) {
        //創建真實角色
        You you = new You();
        //創建代理角色+真實角色的引用
        WeddingCompany company = new WeddingCompany();
        //執行任務
        company.marry();

    }
}
interface Marry{
    public abstract void marry();
}
//真實角色
class You implements Marry{

    @Override
    public void marry() {
        System.out.println("you and 嫦娥結婚了");
    }
}
//代理角色
class WeddingCompany implements Marry{

    private Marry you;

    public WeddingCompany(Marry you) {
        this.you = you;
    }
    public WeddingCompany() {

    }
    private void before(){
        System.out.println("佈置豬窩");
    }
    private void after(){
        System.out.println("鬧玉兔");
    }

    @Override
    public void marry() {
        before();
        you.marry();
        after();
    }
}

感覺靜態代理模式是否也應用於了測試用例部分,比如Junit的設置前置與後置條件。

通過Calllable接口實現多線程

Callable和Future接口

Callable和Runnable有幾點不同:

1.Callable規定的方法是call(),Runnable規定的方法是run()

2.call方法可以拋出異常,run方法不能拋出異常

3.Callable任務執行後可返回值,運行Callable任務可拿到一個Future對象,而Runnable任務是不能返回值的,Future表示異步計算結果,它提供了檢查計算是否完成的方法,以等待計算的完成,並檢索計算的結果。

通過Future對象可瞭解任務執行情況,可取消任務的執行,還可獲取任務執行的結果

package com.zjn.thread.create;

import java.util.concurrent.*;

/**
 * 使用callable創建線程
 */
public class Call {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //創建線程
        ExecutorService ser = Executors.newFixedThreadPool(1);
        Race tortoise = new Race("老烏龜",1000);
        Race rabbit = new Race("小兔子",500);
        //獲取值
        Future<Integer> result1 = ser.submit(tortoise);
        Future<Integer> result2 = ser.submit(rabbit);

        Thread.sleep(2000);//2秒

        //停止線程體循環
        tortoise.setFlag(false);
        rabbit.setFlag(false);
        int num1 = result1.get();
        int num2 = result2.get();
        System.out.println("烏龜跑了-->"+num1+"步");
        System.out.println("兔子跑了-->"+num2+"步");
        //停止服務
        ser.shutdown();
    }
}
class Race implements Callable<Integer>{

    private String name;//名稱
    private long time;//延時時間
    private boolean flag = true;
    private int step = 0;//步

    @Override
    public Integer call() throws Exception {
        while (flag){
            Thread.sleep(time);//延時
            step++;
        }
        return 1000;
    }

    ...//構造方法以及setter、getter類

線程的狀態和方法

新生狀態 就緒狀態 運行狀態 阻塞狀態 死亡狀態

停止線程

1.自然終止:線程體正常執行完畢

2.外部干涉:

  1. 線程類中 定義 線程體使用的標識

2)線程體使用該標識

  1. 提供對外的方法改變該標識

4)外部根據條件調用該方法

阻塞

1.join:合併線程

package com.zjn.thread.status;

/**
 * join:合併線程
 */
public class JoinDemo1 extends Thread{
    public static void main(String[] args) throws InterruptedException {

        JoinDemo1 demo = new JoinDemo1();
        Thread t = new Thread(demo);
        t.start();
        //cpu調度運行
        for (int i=0;i<100;i++){
            if(50==i){
                t.join();//main阻塞
            }
            System.out.println("main... "+i);
        }
    }

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

2.yield:暫停自己的線程

package com.zjn.thread.status;

public class YieldDemo01 extends Thread {
    public static void main(String[] args) {
        JoinDemo1 demo = new JoinDemo1();
        Thread t = new Thread(demo);
        t.start();
        //cpu調度運行
        for (int i=0;i<100;i++){
            if(i%20==0){
                //暫停本線程main
                Thread.yield();
            }
            System.out.println("main... "+i);
        }
    }

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

3.sleep:休眠,不釋放鎖。排他鎖

1)與時間相關:倒計時

package com.zjn.thread.status;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 倒計時
 * 1.倒數10個數,1秒內打印一個
 * 2.倒計時
 */
public class SleepDemo01 {
    public static void main(String[] args) throws InterruptedException {
        Date endTime = new Date(System.currentTimeMillis()+10*1000);
        long end = endTime.getTime();
        while(true){
            //輸出
            System.out.println(new SimpleDateFormat("mm:ss").format(endTime));
            //構建下一秒時間
            endTime = new Date(endTime.getTime()-1000);
            //等待一秒
            Thread.sleep(1000);
            //10秒以內繼續,否則退出
            if(end-10000>endTime.getTime()){
                break;
            }
        }
    }
    public static void test1() throws InterruptedException {
        int num = 10;
        while(true) {
            System.out.println(num);
            Thread.sleep(1000);//暫停
            if(num<0){
                break;
            }
        }
    }
}

2)模擬網絡延時

package com.zjn.thread.status;

/**
 * Sleep模擬網絡延時,線程不安全
 */
public class SleepDemo02 {
    public static void main(String[] args) {
        //真實角色
        com.zjn.thread.create.Web12306 web = new com.zjn.thread.create.Web12306();
        //代理
        Thread t1 = new Thread(web,"路人甲");
        Thread t2 = new Thread(web,"黃牛乙");
        Thread t3 = new Thread(web,"工程師");

        t1.start();
        t2.start();
        t3.start();
    }
}
class Web12306 implements Runnable {
    private int num = 50;
    @Override
    public void run() {
        while(true){
            if (num < 0){
                break; //跳出循環
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"搶到了"+num--);
        }
    }
}
  • 線程基本信息和優先級
package com.zjn.thread.info;

/**
 * 優先級:概率,不是絕對的優先級,沒有絕對先後順序
 * MAX_PRIORITY 10
 * NORM_PRIORITY 5 默認
 * MIN_PRIORITY 1
 */
public class InfoDemo02 {

    public static void main(String[] args) throws InterruptedException {
        MyThread it = new MyThread();
        Thread p1 = new Thread(it,"挨踢1");
        MyThread it2 = new MyThread();
        Thread p2=new Thread(it2,"挨踢2");

        p1.setPriority(Thread.MIN_PRIORITY);//設置優先級
        p2.setPriority(Thread.MAX_PRIORITY);
        p1.start();
        p2.start();

        Thread.sleep(1000);
        it.stop();
        it2.stop();
    }
}

package com.zjn.thread.info;

/**
 * Thread currentThread() :當前線程
 * setName() :設置名稱
 * getName() :獲取名稱
 * isAlive()
 */
public class InfoDemo01 {
    public static void main(String[] args) throws InterruptedException {
        MyThread it = new MyThread();
        Thread proxy = new Thread(it,"挨踢");
        proxy.setName("test");
        System.out.println(proxy.getName());
        System.out.println(Thread.currentThread().getName());//main
        proxy.start();
        System.out.println("啓動後的狀態"+proxy.isAlive());
        Thread.sleep(200);
        it.stop();
        Thread.sleep(100);
        System.out.println("停止後的狀態"+proxy.isAlive());
    }
}

package com.zjn.thread.info;

public class MyThread implements Runnable {
    private boolean flag = true;
    private int num = 0;
    @Override
    public void run() {
        while(flag) {
            System.out.println(Thread.currentThread().getName()+"-->"+ num++);
        }
    }
    public void stop(){
        this.flag = !this.flag;
    }
}

線程的同步和死鎖問題

同步:併發,多個線程訪問同一份資源 確保資源安全 -> 線程安全

Synchronize -> 同步

一、同步塊

synchronized(引用類型|類|class){

}

死鎖:過多的同步會造成死鎖

package com.zjn.thread.syn;

public class SynDemo01 {
    public static void main(String[] args) {
        //真實角色
        com.zjn.thread.create.Web12306 web = new com.zjn.thread.create.Web12306();
        //代理
        Thread t1 = new Thread(web,"路人甲");
        Thread t2 = new Thread(web,"黃牛乙");
        Thread t3 = new Thread(web,"工程師");

        t1.start();
        t2.start();
        t3.start();
    }
}
class Web12306 implements Runnable {
    private int num = 50;
    private boolean flag = true;
    @Override
    public void run() {
        while (flag) {
            test2();
          }

    }
    //鎖定範圍不正確
    public void test4(){
        //a,b,c
        synchronized (this) {
            if (num < 0) {
                flag=false; //跳出循環
                return;
            }
        }
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "搶到了" + num--);
    }
    //線程安全 鎖定正確
    public void test2(){
        if (num < 0) {
            flag=false; //跳出循環
            return;
        }
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "搶到了" + num--);
    }
    //線程不安全
    public synchronized void test1(){
        if (num <= 0) {
            flag=false; //跳出循環
        }
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "搶到了" + num--);

    }
    //線程安全 鎖定正確
    public void test5(){
        //a,b,c
        synchronized ((Integer)num) {
            if (num < 0) {
                flag=false; //跳出循環
                return;
            }
        }
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "搶到了" + num--);
    }

}

單例設計模式

1.懶漢式
2.餓漢式

package com.zjn.thread.syn;

/**
 * 單例設計模式:外部使用時,確保一個類只有一個對象
 */
public class SynDemo02 {
    public static void main(String[] args) {
//        //單線程中地址空間一樣
//        Jvm jvm1 = Jvm.getInstance();
//        Jvm jvm2 = Jvm.getInstance();
//        System.out.println(jvm1);
//        System.out.println(jvm2);
//        //多線程地址空間不同
//        JvmThread thread1 = new JvmThread(100);
//        JvmThread thread2 = new JvmThread(500);
//        thread1.start();
//        thread2.start();
        //加入同步,地址空間一樣
        JvmThread thread1 = new JvmThread(100);
        JvmThread thread2 = new JvmThread(500);
        thread1.start();
        thread2.start();
    }
}
class JvmThread extends Thread{
    private long time;

    public JvmThread() {

    }

    public JvmThread(long time) {
        this.time = time;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->創建:"+Jvm.getInstance(time));
    }
}
/**
 * 單例設計模式
 * 確保一個類只有一個對象
 * 懶漢式-使用時才創建對象
 * 1。構造器私有化,避免外部直接創建對象
 * 2。聲明一個私有的靜態變量
 * 3.創建一個對外的公共的靜態方法訪問該變量,如果變量沒有對象,創建該對象
 */
class Jvm {
    //聲明一個私有的靜態變量,靜態的同一份資源
    private static Jvm instance = null;
    //構造器私有化,避免外部直接創建對象
    private Jvm(){

    }

    //double checking雙重檢查提高已經存在對象的訪問效率
    public static Jvm getInstance(long time) {
        if (null==instance){
            //其他線程進入的時候如果發現已經有對象就不用重新創建,直接獲取
            synchronized (Jvm.class){
                if (null == instance) {
                    try {
                        Thread.sleep(time);//延時,放大錯誤
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    instance = new Jvm();
                }

            }
        }
        return instance;
    }
    //synchronized鎖類的字節碼信息
    public static Jvm getInstance3(long time) {
        //a b --> 效率不高,進來都需要等待,存在對象也需要等待
        synchronized (Jvm.class){
            if (null == instance) {
                try {
                    Thread.sleep(time);//延時,放大錯誤
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                instance = new Jvm();
            }
            return instance;
        }

    }
    public static synchronized Jvm getInstance2(long time) {
        if (null == instance) {
            try {
                Thread.sleep(time);//延時,放大錯誤
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            instance = new Jvm();
        }
        return instance;
    }
    //創建一個對外的公共的靜態方法訪問該變量,如果變量沒有對象,創建該對象
    public static Jvm getInstance1(long time) {
        if (null == instance) {
            try {
                Thread.sleep(time);//延時,放大錯誤
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            instance = new Jvm();
        }
        return instance;
    }
}
package com.zjn.thread.syn;

/**
 * 單例創建的方式
 * 1。懶漢式
 * 1)構造器私有化
 * 2)聲明私有的靜態屬性
 * 3)對外提供訪問屬性的靜態方法,確保該對象存在
 */
public class MyJvm {
    private static MyJvm instance;
    private MyJvm(){

    }
    public static MyJvm getInstance(){
        if(null==instance){ // 提高效率
            synchronized (MyJvm.class){
                if(null==instance){ //安全檢查
                    instance = new MyJvm();
                }
            }
        }
        return instance;
    }
}

/**
 * 餓漢式-線程安全
 *  1)構造器私有化
 *  2)聲明私有的靜態屬性,同時創建該對象
 *  3)對外提供訪問屬性的靜態方法
 */
class MyJvm2 {
    //類加載時創建
    private static MyJvm2 instance = new MyJvm2();
    private MyJvm2(){

    }
    public static MyJvm2 getInstance(){
        return instance;
    }
}

/**
 * 提高效率
 * 類在使用的時候加載,JVMholder延緩了加載時間
 */
class MyJvm3 {
    private static class JVMholder{
        //類加載時創建
        private static MyJvm3 instance = new MyJvm3();
    }

    private MyJvm3(){

    }
    public static MyJvm3 getInstance(){
        return JVMholder.instance;
    }
}

死鎖

package com.zjn.thread.syn;

/**
 * 過多的同步方法可能造成死鎖
 */
public class SynDemo03 {
    public static void main(String[] args) {
        Object g = new Object();
        Object m = new Object();
        //兩個線程訪問同樣資源
        Test t1 = new Test(g,m);
        Test2 t2 = new Test2(g,m);
        Thread proxy = new Thread(t1);
        Thread proxy2 = new Thread(t2);
        proxy.start();
        proxy2.start();

class Test implements Runnable{
    Object goods;
    Object money;

    public Test(Object goods, Object money) {
        this.goods = goods;
        this.money = money;
    }

    @Override
    public void run() {
        while (true){
            test();
        }
    }
    public void test(){
        synchronized (goods){
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (money){

            }
        }
        System.out.println("一手給錢");
    }
}

class Test2 implements Runnable{
    Object goods;
    Object money;

    public Test2(Object goods, Object money) {
        this.goods = goods;
        this.money = money;
    }

    @Override
    public void run() {
        while (true){
            test();
        }
    }
    public void test(){
        synchronized (money){
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (goods){

            }
        }
        System.out.println("一手給貨");
    }
}

生產者消費者模式-信號燈法

Producer-consumer problem生產者-消費者問題,也稱爲有限緩衝問題,是一個多線程同步問題的經典案例。該問題描述了兩個共享固定大小緩衝區的線程-即所謂生產者和消費者——在實際運行時會發生的問題,生產者的主要作用是生成一定量的數據放到緩衝區中,然後重複此過程。與此同時,消費者也在緩衝區消耗這些數據。該問題的關鍵就是要保證生產者不會在緩衝區滿時加入數據,消費者也不會在緩衝區空時消耗數據。

要解決該問題,就必須讓生產者在緩衝區滿時休眠(要麼乾脆放棄數據)。等到下次消費者消耗緩衝區中的數據的時候,生產者才能被喚醒,開始往緩衝區中添加數據。同樣,也可以讓消費者在緩衝區空時進入休眠,等到生產者往緩衝區添加數據之後,再喚醒消費者。通常常用的方法有信號燈法、管程等。如果解決方法不夠完善,則容易出現死鎖的情況。出現死鎖時,兩個線程都會陷入休眠,等待對方喚醒自己。

package com.zjn.thread.pro;

public class App {
    public static void main(String[] args) {
        //共同的資源
        Movie m = new Movie();

        //多線程
        Player p = new Player(m);
        Watcher w = new Watcher(m);

        new Thread(p).start();
        new Thread(w).start();
    }
}

package com.zjn.thread.pro;

/**
 * 一個場景,共同的資源
 * 生產者消費者模式 信號燈法
 * wait():等待,釋放鎖。sleep不釋放鎖
 * notify()/notifyAll():喚醒
 * 與synchronized一起使用
 */
public class Movie {
    private String pic;
    //信號燈
    //flag=true生產者生產,消費者等待,生產完成後通知消費
    //flag=flag生產者等待,消費者消費,消費完成後通知生產
    private boolean flag = true;
    /**
     * 播放
     * @param pic
     */
    public synchronized void play(String pic){
        if(!flag){//生產者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        //開始生產
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("生產了"+pic);
        //生產完畢
        this.pic = pic;
        //通知消費
        this.notify();
        //生產者停下
        this.flag = false;
    }
    public synchronized void watch(){
        if (flag){//消費者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //開始消費
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("消費了"+pic);
        //消費完畢
        //通知生產
        this.notifyAll();
        this.flag = true;
    }
}

package com.zjn.thread.pro;

/**
 * 生產者
 */
public class Player implements Runnable{
    private Movie m;

    public Player(Movie m) {
        this.m = m;
    }
    @Override
    public void run() {
        for(int i=0;i<20;i++){
            if(i%2==0){
                m.play("左青龍");
            }else{
                m.play("右白虎");
            }
        }
    }
}

package com.zjn.thread.pro;

/**
 * 消費者
 */
public class Watcher implements Runnable{
    private Movie m;

    public Watcher(Movie m) {
        this.m = m;
    }

    @Override
    public void run() {
        for(int i=0;i<20;i++){
            m.watch();
        }
    }
}

任務調度

Timer定時器類

TimerTask任務類

通過java timer timertask:(spring的任務調度就是通過他們來實現的)

在這種實現方式中,Timer類實現的是類似鬧鐘的功能,也就是定時或是每隔一定時間觸發一次線程。其實,Timer類本身實現的就是一個線程,只是這個線程是用來實現調用其他線程的。而TimerTask類是一個抽象類,該類實現了Runnable接口,所以按照前面的介紹,該類具備多線程的能力。

在這種實現方式中,通過繼承TimerTask使該類獲得多線程的能力,將需要多線程執行的代碼書寫在run方法內部,然後通過Timer類啓動線程的執行

在實際使用時,一個Timer可以啓動任意多個TimerTask實現的線程,但是多個線程之間會存在阻塞。所以如果多個線程之間如果需要完全獨立運行的話,最好還是一個Timer啓動一個TimerTask實現。

package com.zjn.thread.schedule;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * schedule(TimerTask task, Date time)
 * schedule(TimerTask task, Date time, long period)
 */

public class TimeDemo01 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        //每隔200毫秒運行一次
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("so easy...");
            }
        },new Date(System.currentTimeMillis()+1000),200);
    }
}

Python多線程

啓動線程

	# 創建線程
    x = threading.Thread(target=thread_function, args=(1,))
    # 啓動線程
    x.start()

守護進程和線程

要告訴一個線程等待另一個線程結束,請調用.join()

x.join()

啓動多線程

使用threading.Thread

線程的運行順序由操作系統確定,可能很難預測。它可能(並且可能會)因運行而異

    threads = list()
    for index in range(3):
        x = threading.Thread(target=thread_function, args=(index,))
        threads.append(x)
        x.start()

    for index, thread in enumerate(threads):
        thread.join()

使用ThreadPoolExecutor

作爲上下文管理器,使用該with語句來管理池的創建和銷燬

import concurrent.futures
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
        executor.map(thread_function, range(3))

競爭問題

當兩個或多個線程訪問共享的數據或資源時,可能會發生爭用情況。

Lock

一次只允許一個線程進入代碼的read-modify-write部分。最常見的方法是Lock在Python中調用。在其他一些語言中,相同的想法稱爲mutex。Mutex來自互斥,這正是a的Lock功能。
由with語句鎖定和釋放:

class FakeDatabase:
    def __init__(self):
        self.value = 0
        self._lock = threading.Lock()

    def locked_update(self, name):
        with self._lock:
            local_copy = self.value
            local_copy += 1
            time.sleep(0.1)
            self.value = local_copy
           
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章