【Java/Python】多线程

多线程编程基础

  • Java多线程
    • 创建线程
      • 继承Thread + 重写run(线程体)
      • 通过Runnable接口实现多线程
      • 静态代理设计模式
      • 通过Calllable接口实现多线程
    • 线程的状态和方法
      • 停止线程
      • 阻塞
    • 线程的同步和死锁问题
      • 单例设计模式
    • 死锁
    • 生产者消费者模式-信号灯法
    • 任务调度
  • Python多线程
    • 启动线程
      • 守护进程和线程
    • 启动多线程
      • 使用threading.Thread
      • 使用ThreadPoolExecutor
    • 竞争问题
      • Lock

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
           

你可能感兴趣的:(java复习)