目录
前言
1.入门多线程
1.1. 线程、进程、多线程、线程池
1.2.并发、串行、并行
1.3. 线程的实现方式
1.3.1. 继承 Thread 类
1.3.2. 实现 Runnable 接口
1.3.3. 使用 Callable 和 Future
1.3.4. 使用线程池
1.4.线程的状态
1.5. 线程常用方法
1.5.1 sleep()
1.4.2 join()
1.5.3 yield()
1.5.4.wait() 和 notify()
1.5.5.-interrupt()方法和stop()方法
1.5.6. setPriority (int newPriority)、getPriority()
1.6.线程调度
2.进阶多线程
2.1.多线程引发问题
2.2.多线程解决方法
2.2.1. 锁机制
2.2.3. 线程池
2.2.4. 并发工具类
3.面试题
3.1.sleep()和wait()有什么区别?
3.2.如何停止一个处在运行态的线程
3.3.interrupt()、interrupted()、isInterrupted()方法的区别
3.4.系统创建的线程过多会有什么影响
4.写在最后
鸣谢
多线程已经成为一种常见的编程模式,广泛应用于各种不同类型的应用程序中。
本篇博客文章中,我们将会探讨多线程编程的相关知识和技巧。通过代码示例和实际应用案例来深入了解多线程的具体实现和应用方法,帮助更好地掌握多线程编程技术,提高程序效率和性能。后期随学习深入还会补充修改。
线程
在计算机科学中,线程是指进程中的一个单独的执行路径。一个进程可以包含多个线程,每个线程都可以并行执行不同的任务。多线程编程是指在同一时间内运行多个线程来完成多个任务。
多线程
多线程是指在同一时间内运行多个线程来完成多个任务。多线程提高程序的性能和响应速度。但是增加了代码的复杂性,同时需要考虑线程安全和死锁等问题。
线程池
线程池是一组预先创建的线程,它们可以被重复使用来执行多个任务。使用线程池可以避免在创建和销毁线程时产生额外的开销,从而提高程序的性能。Java 中提供了 Executor 框架来实现线程池。
进程
进程即一段程序的执行过程,是计算机中的程序关于某数据集合上的一次运行活动,是系统分配资源的最小单位
线程与进程的区别
并发、串行、并行概念
并发、串行、并行的区别
并发编程的三要素
多线程和并发
多线程是指在同一时间内运行多个线程来完成多个任务。多线程可以提高程序的性能和响应速度,因为它们可以同时执行多个任务。
并发是指在同一时间内执行多个任务的能力。并发可以通过使用多线程来实现,但也可以通过其他方式实现,例如使用异步编程或事件驱动编程。
因此,多线程是实现并发的一种方式,但并发不一定需要使用多线程。另外,多线程编程中需要考虑的问题,例如线程安全和死锁等,在并发编程中同样需要考虑
多线程是提高程序性能和响应速度的重要手段,Java 中有多种实现方式,
class MyThread extends Thread {
public void run() {
// 执行需要的代码
}
}
MyThread thread = new MyThread();
thread.start();
- 介绍:通过实现 Runnable 接口来实现多线程。
- 示例代码:展示如何实现 Runnable 接口并重写 run() 方法。
- 优点:可以继承其他类,因为 Java 支持实现多个接口。
- 缺点:需要创建 Thread 对象来启动线程。
- 步骤:
- 自定义线程类实现Runnable接口
- 实现run()方法,编写线程体
- 创建线程对象,调用start()方法启动线程(启动后不一定立即执行,抢到CPU资源才能执行)
class MyRunnable implements Runnable {
public void run() {
// 执行需要的代码
}
}
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
- 介绍:通过使用 Callable 和 Future 接口来实现多线程。
- 示例代码:展示如何使用 Callable 和 Future 接口来创建和启动线程。
- 优点:可以获取线程执行的返回值。
- 缺点:相比于前两种方式,实现稍微复杂一些。
- 步骤:
- 实现Callable接口,先要返回值类型
- 重写call()方法,需要抛出异常
- 创建目标对象
- 创建执行服务:ExecutorService ser = Executor.newFixedThreadPool(1);
- 提交执行:Futureres = ser.submit(t1);
- 获取结果:boolean r1 = res.get();
- 关闭服务:ser.shutdownNow();
import java.util.concurrent.*;
// 自定义线程对象,实现Callable接口,重写call()方法
public class MyThread implements Callable {
@Override
public Boolean call() throws Exception {
// 线程执行体
for (int i = 0; i < 10; i++) {
System.out.println("我是自定义" + Thread.currentThread().getName() + "--" + i);
}
return true;
}
public static void main(String[] args) throws ExecutionException,
InterruptedException {
// main线程,主线程
// 创建线程实现类对象
MyThread thread = new MyThread();
MyThread thread2 = new MyThread();
// 创建执行服务,参数是线程池线程数量
ExecutorService ser = Executors.newFixedThreadPool(2);
// 提交执行
Future res = ser.submit(thread);
Future res2 = ser.submit(thread2);
// 获取结果
boolean r1 = res.get();
boolean r2 = res2.get();
// 关闭服务
ser.shutdownNow();
}
}
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.execute(new Runnable() {
public void run() {
// 执行需要的代码
}
});
sleep() 方法可以使当前线程暂停指定的时间。例如:
try {
Thread.sleep(1000); // 暂停 1 秒钟
} catch (InterruptedException e) {
e.printStackTrace();
}
join() 方法可以等待指定的线程执行完毕。例如:
Thread thread1 = new Thread(() -> {
// 执行需要的代码
});
Thread thread2 = new Thread(() -> {
// 执行需要的代码
});
thread1.start();
thread2.start();
try {
thread1.join(); // 等待 thread1 执行完毕
thread2.join(); // 等待 thread2 执行完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
暂停当前正在执行的线程对象,并执行其他线程,yield() 方法可以让出 CPU 时间片,使其他线程有机会运行。
例如:
Thread.yield();
yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。
在多线程程序中,可以使用 wait()
和 notify()
方法来协调多个线程之间的操作。wait()
方法可以使当前线程等待另一个线程发出通知,而 notify()
方法可以通知等待的线程继续执行。
public class Message {
private String content;
private boolean available = false;
public synchronized String getContent() {
while (!available) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
available = false;
notifyAll();
return content;
}
public synchronized void setContent(String content) {
while (available) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.content = content;
available = true;
notifyAll();
}
}
public class Main {
public static void main(String[] args) {
Message message = new Message();
// 创建两个线程来发送和接收消息
new Thread(() -> {
message.setContent("Hello");
}).start();
new Thread(() -> {
System.out.println(message.getContent()); // 输出 Hello
}).start();
}
}
- JDK提供的stop方法已废弃,不推荐使用。
- 推荐线程自动停止下来,建议使用一个标识位变量进行终止,当flag=false时,则终止线程运行。
public class DemoInterrupt {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable2());
t.setName("t");
t.start();
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 终断t线程的睡眠(这种终断睡眠的方式依靠了java的异常处理机制。)
t.interrupt();
// t.stop(); //强行终止线程
//缺点:容易损坏数据 线程没有保存的数据容易丢失
}
}
class MyRunnable2 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "---> begin");
try {
// 睡眠1天
Thread.sleep(1000 * 60 * 60 * 24);
} catch (InterruptedException e) {
// e.printStackTrace();
}
//1天之后才会执行这里
System.out.println(Thread.currentThread().getName() + "---> end");
}
}
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。 - 线程的优先级用数据表示,范围1~10。 - 线程的优先级高只是表示他的权重大,获取CPU执行权的几率大。 - 先设置线程的优先级,在执行start()方法
public class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程优先级:"
+ Thread.currentThread().getPriority());
}
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread,"a");
Thread thread2 = new Thread(myThread,"b");
Thread thread3= new Thread(myThread,"c");
Thread thread4= new Thread(myThread,"d");
thread3.setPriority(Thread.MAX_PRIORITY);
thread.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(Thread.NORM_PRIORITY);
thread4.setPriority(8);
thread.start();
thread2.start();
thread3.start();
thread4.start();
}
}
结果如下:
c线程优先级:10
b线程优先级:5
a线程优先级:1
d线程优先级:8
线程调度模型
均分式调度模型:所有的线程轮流使用CPU的使用权,平均分配给每一个线程占用CPU的时间。
抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么就会随机选择一个线程来执行,优先级高的占用CPU时间相对来说会高一点点。
Java中JVM使用的就是抢占式调度模型
getPriority()
:获取线程优先级
setPriority
:设置线程优先级
多线程编程能够提高程序的性能和响应能力,但同时也会带来一些问题。主要包括以下几个方面:
1.竞态条件(Race Condition):当多个线程同时访问共享资源时,由于线程执行顺序的不确定性,可能会导致程序的输出结果出现错误。例如,多个线程同时对一个计数器进行自增操作,如果没有进行同步,可能会导致计数器的值不正确。
可以使用 synchronized 关键字来解决竞态条件问题。
2.死锁(Deadlock):当多个线程相互等待对方释放所占用的资源时,可能会陷入死锁状态,无法继续执行。例如,线程 A 占用了资源 1,等待资源 2,而线程 B 占用了资源 2,等待资源 1,两个线程都无法继续执行。
可以使用锁和条件变量来解决死锁问题。
3.饥饿(Starvation):当某些线程由于竞争共享资源失败而无法继续执行时,可能会出现饥饿问题。例如,如果一个线程在一个高负载的系统中请求资源,它可能会等待很长时间才能获得所需的资源。
4.上下文切换:当多个线程同时运行时,操作系统需要进行上下文切换,这会消耗一定的系统资源。如果线程数量过多,上下文切换的开销可能会超过程序本身的开销
5.线程安全:在多线程程序中,需要确保共享资源的安全性。可以使用锁和原子变量来实现线程安全。
6.性能优化:在多线程程序中,需要考虑性能优化问题。可以使用线程池和并发集合来提高程序性能。
两者都可暂停当前线程
此时如果线程在阻塞状态:
那么就会抛出InterruptedException异常,并重置中断标志位
如果线程不在阻塞状态:
使用Thread.interrupted()判断当前中断标志位是否被设置,并重置中断标志位
使用Thread.currentThread.isInterrupted()判断当前中断标志位是否被设置,不重置中断标志位
以上就是我对多线程的个人简介,后续会不断完善更新,与大家共勉
[1] https://blog.csdn.net/zdl66/article/details/126297036
[2] https://blog.csdn.net/m0_46233999/article/details/118702235
[3] https://blog.csdn.net/qq_29141913/article/details/125964815#3_16
[4] https://blog.csdn.net/YQQAGH178/article/details/119828128