多线程讲解(一)

本文详细介绍了Java中的多线程概念,包括进程与线程的区别、线程的创建与执行,以及线程同步问题。通过实例演示了继承Thread类和实现Runnable接口两种创建线程的方式,并讨论了它们的优缺点。此外,还提到了Callable接口的使用,以及多线程在实际问题中如买火车票和龟兔赛跑案例的应用。最后,文章强调了线程安全和资源管理的重要性。

目录标题

多线程简介

在这里插入图片描述

多任务

先来了解一下多任务

在这里插入图片描述

进程

process 进程
Thread 线程

在下面的图片中我们可以看到:当视频(进程process)开始播放的时候
可以看到 听到 声音和图像 字幕 这些线程(Thread)是同时进行的

在这里插入图片描述

process和Thread的区别



在这里插入图片描述




什么是线程

Thread 线程
线程就是CPU调度和执行的单位
通常一个进程中包含若干个线程

在这里插入图片描述

什么是多线程

在这里插入图片描述

普通方法调用和多线程调用的例子(重点)

在这里插入图片描述

线程创建的方法(重点)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

练习一

创建线程的方法一: 继承Thread类 重写run()方法 调用start方法开启线程



//重点: 调用 start方法时 是同时运行的
// 重点:(当你运行的东西比较少时 不容易观察出来是同时运行的)
// (可以将for循环改为 两千来调用试试)
// for (int i = 0; i < 2000; i++) { }
//而调用 run 方法时 是由先后顺序决定的
//调用start方法时 main方法 和线程中start方法是同时运行的(交替执行)
//重点: 当两个方法同时运行的 线程不一定同时运行 (而是看cpu调度安排)
TestThread1 testThread1 = new TestThread1();
testThread1.start();
//testThread1.run();




在这里插入图片描述

理解代码图片

在这里插入图片描述

练习二

练习 Thread 实现多线程同步下载图片

在这里插入图片描述

练习三

创建线程的第二种方式(实现Runnable接口)

在这里插入图片描述

重点:
//创建线程方式二: 实现runnable接口 重写run方法
// 执行线程需要丢入runnable接口实现类 调用start方法



//创建runnable接口的实现类对象
TestThread3 testThread3 = new TestThread3();



//创建线程对象 通过线程对象来开启我们的线程(代理功能)
Thread thread = new Thread(testThread3);
thread.start();
//new Thread(testThread3).start();//意思为上面两行代码的简写



在这里插入图片描述

重点总结:
继承Thread类 时只有一份资源
实现Runnable接口 时一份资源但多个代理
原因推荐使用实现Runnable接口创建线程
因为面向对象(oop)继承类的具有局限性 使用不方便灵活
因为Java的类只有单继承 extends 而接口可以多继承 方便灵活实现接口

在这里插入图片描述

实现Runnable接口和继承Thread类的区别:

因为Java是单继承 所以推荐使用Runnable接口

在这里插入图片描述

创建多线程练习

练习 多个线程同时操作同一个对象 买火车票的例子




发现问题:多个线程操作一个资源的情况下 线程不安全 数据紊乱




重点
System.out.println(Thread.currentThread().getName()+“–>拿到了第”+ticketNums–+“票”);

在这里插入图片描述

案例一 龟兔赛跑

在这里插入图片描述

//创建胜利者
//重点
//static 保证只有一个冠军winner
//因为:静态变量不属于对象 (static只执行一次)
//加入static 保证了冠军的唯一性
private static String winner;




//判断是否完成比赛
//重点:参考 顺序结构和选择结构详解
//复习:1 当一个if语句中由最多只能有一个else语句 而且else语句种必须在所有的else if语句之后
// 2 if语句中可以有若干个else if语句 而且它们必须在else语句之前 一旦其中一个else if语句中 检测结果为true 其他的else if
//以及else语句都将跳过执行
//3 return的意思为结束方法和返回一个结果
//4 方法的调用(复习)如果方法有返回值时则写出返回值类型 如 int类型 byte等类型
//如果方法没有返回值则用void 而且如果方法返回值是void
//那么方法调用的一定是一条语句
private boolean gameOver(int steps){
if ((winner != null)) {
//已经存在胜利者
return true;
}else {
if (steps>=100){
winner=Thread.currentThread().getName();
System.out.println(“winner is”+winner);
return true;
}
}
return false;
}




在这里插入图片描述

实现Callable接口(了解即可)

线程的创建方法三:实现Callable接口

在这里插入图片描述

重要四步骤


//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);



//提交执行
Future r1=ser.submit(t1);
Future r2=ser.submit(t2);
Future r3=ser.submit(t3);



//获取结果
boolean rs1=r1.get();
boolean rs2=r2.get();
boolean rs3=r3.get();



//打印返回值
System.out.println(rs1);
System.out.println(rs2);
System.out.println(rs3);



//关闭服务
ser.shutdown();



实现 Callable接口创建线程的好处
1 可以定义返回值(Boolean 等类型)
2 可以抛出异常



在这里插入图片描述

在这里插入图片描述

<think>我们正在回答用户关于Python多线程详细讲解的问题。根据用户提供的引用内容,我们需要整合这些信息,并按照系统指令的要求进行回答。 引用[1]提到Python多线程用于提高程序运行效率,但作者后来意识到Python多线程机制的些内情(暗示可能有些限制或误解)。 引用[2]给出了多线程的示例代码,展示了如何使用threading模块创建线程、启动线程以及使用join()和setDaemon()方法。 引用[3]解释了多线程的概念,即在主线程中运行多个子线程,它们属于同个进程,并提到了主线程与子线程的关系。 结合这些引用,我们可以给出以下讲解: 1. Python多线程的基本概念:使用threading模块,可以创建多个线程(子线程)在同个进程中运行,主线程负责执行主程序。 2. 线程的创建:通过创建threading.Thread对象,指定目标函数和参数。 3. 线程的启动:调用start()方法启动线程。 4. 线程的同步:使用join()方法可以让主线程等待子线程完成。 5. 守护线程:通过setDaemon(True)将子线程设置为守护线程,当主线程结束时,守护线程也会随之结束。 另外,需要特别注意的是,由于GIL(全局解释器锁)的存在,Python的多线程并不适合计算密集型的任务,因为同时刻只有个线程在执行Python字节码。但对于I/O密集型任务,多线程仍然可以提高效率。 下面我们将按照用户要求,给出个详细的讲解,包括示例代码(按照用户提供的引用[2]中的代码,但会做适当调整和注释)以及相关概念的说明。 注意:在回答中,我们将使用中文,并确保行内公式(如果有的话)用$...$,独立公式用$$...$$(尽管多线程讲解可能较少涉及数学公式,但如果有必要我们会遵循)。同时,在回答末尾生成相关问题。 由于用户要求生成相关问题,我们将在最后生成几个相关问题。 现在开始编写回答: </think>### Python多线程详细讲解 Python的多线程通过`threading`模块实现,它允许在单个进程中并发执行多个线程。但由于GIL(全局解释器锁)的限制,Python多线程**更适用于I/O密集型任务**(如网络请求、文件读写),而非CPU密集型任务(如数值计算)。以下是核心概念和用法详解: --- #### **1. 基本使用** ```python import threading import time def task(name, delay): print(f"{name}开始执行: {time.ctime()}") time.sleep(delay) # 模拟I/O操作 print(f"{name}结束执行: {time.ctime()}") # 创建线程 t1 = threading.Thread(target=task, args=("线程1", 2)) t2 = threading.Thread(target=task, args=("线程2", 3)) # 启动线程 t1.start() t2.start() # 等待线程结束 t1.join() t2.join() print(f"主线程结束: {time.ctime()}") ``` - **`Thread()`参数**: - `target`:线程执行的函数(函数名,不加括号) - `args`:函数参数的元组形式 - **`start()`**:启动线程 - **`join()`**:阻塞主线程,直到子线程完成[^2] --- #### **2. 守护线程(Daemon Thread)** ```python t = threading.Thread(target=task, args=("守护线程", 5)) t.setDaemon(True) # 设置为守护线程 t.start() print("主线程直接结束,不等待守护线程") ``` - 守护线程会随主线程结束而强制终止 - 适用于后台任务(如日志监控),无需等待其完成[^2] --- #### **3. 线程同步与锁机制** 当多个线程修改共享数据时,需使用锁避免竞争: ```python counter = 0 lock = threading.Lock() def increment(): global counter with lock: # 自动获取和释放锁 for _ in range(100000): counter += 1 threads = [] for _ in range(5): t = threading.Thread(target=increment) threads.append(t) t.start() for t in threads: t.join() print(counter) # 正确输出500000 ``` - **`Lock()`**:确保同时刻只有个线程访问共享资源 - **`with lock`**:上下文管理器自动处理锁的获取/释放 --- #### **4. GIL(全局解释器锁)的局限性** - **GIL本质**:Python解释器次只允许个线程执行Python字节码 - **影响**: - 多线程无法利用多核CPU进行并行计算 - CPU密集型任务使用多进程(`multiprocessing`)更高效 - **适用场景**: - I/O密集型:多线程可显著提升效率(线程在I/O等待时释放GIL) - CPU密集型:需改用多进程[^1][^3] --- #### **5. 线程通信:队列(Queue)** ```python from queue import Queue def producer(q): for i in range(5): q.put(i) print(f"生产: {i}") def consumer(q): while True: item = q.get() if item is None: # 终止信号 break print(f"消费: {item}") q = Queue() t1 = threading.Thread(target=producer, args=(q,)) t2 = threading.Thread(target=consumer, args=(q,)) t1.start() t2.start() t1.join() q.put(None) # 发送终止信号 t2.join() ``` - **线程安全队列**:`Queue`提供阻塞操作(`put()`/`get()`) - 避免手动锁管理,简化生产者-消费者模式 --- ### **关键注意事项** 1. **线程间共享内存**:所有线程可直接访问主线程的全局变量 2. **避免死锁**:按固定顺序获取多个锁 3. **线程局部数据**:用`threading.local()`创建线程私有变量 4. **替代方案**:CPU密集型任务优先考虑`multiprocessing`或`concurrent.futures`模块 > Python的多线程机制在I/O操作中能有效提升效率,但需理解GIL的影响以规避性能陷阱[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值