今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。
我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!
前言
在计算机科学中,多线程是现代操作系统和应用程序的关键概念之一。通过使用多线程,可以同时执行多个任务,提高程序的运行效率。在Java开发语言中,多线程的概念被广泛应用,并且Java提供了丰富的多线程控制机制,使得开发人员能够更好地控制线程的行为。
本文将介绍Java多线程中的线程控制相关内容,包括线程的创建和启动、线程的睡眠和唤醒、线程的等待和通知等方面。通过掌握这些线程控制技术,我们可以更加灵活地处理多线程程序中的并发问题。
摘要
本文将介绍Java多线程中的线程控制相关内容,包括线程的创建和启动、线程的睡眠和唤醒、线程的等待和通知等方面。通过对这些线程控制技术的讲解和实践,我们可以更好地理解和应用多线程编程。
简介
在Java中,线程是最基本的执行单元。通过创建和启动线程,我们可以同时执行多个任务。Java提供了多种方式来创建和启动线程,包括继承Thread类、实现Runnable接口、使用线程池等。
在多线程程序中,线程的睡眠和唤醒是非常重要的操作。线程的睡眠可以通过Thread类的sleep()
方法实现,而线程的唤醒可以通过调用线程的notify()
和notifyAll()
方法来实现。
另外,线程的等待和通知也是多线程编程中常用的技术。线程的等待可以通过调用对象的wait()
方法实现,而线程的通知可以通过调用对象的notify()
和notifyAll()
方法来实现。
源代码解析
线程的创建和启动
在Java中,我们可以通过继承Thread类来创建一个线程类,然后通过调用该线程类的start()
方法来启动线程。下面是一个简单的示例:
package com.example.javase.ms.threadDemo.day3;
/**
* @Author ms
* @Date 2024-04-12 18:27
*/
public class MyRunnable implements Runnable {
public void run() {
// 线程执行的代码
System.out.println("线程执行啦!");
}
}
package com.example.javase.ms.threadDemo.day3;
/**
* @Author ms
* @Date 2024-04-12 18:27
*/
public class ThreadDemo {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
System.out.println("主线程执行啦!");
}
}
上述代码中,我们定义了一个继承自Thread类的MyThread
类,并在该类中重写了run方法。在ThreadDemo 类中创建MyThread
对象,并调用start()
方法来启动线程。
根据如上测试用例,这里我们本地执行一下,结果展示如下:
线程的睡眠和唤醒
案例1:
线程的睡眠可以通过Thread类的sleep()
方法实现。下面是一个示例:
package com.example.javase.ms.threadDemo.day3;
import java.time.LocalTime;
/**
* @Author ms
* @Date 2024-04-12 19:37
*/
public class ThreadSleepTest {
public static void main(String[] args) {
try {
System.out.println("睡眠前:" + LocalTime.now());
Thread.sleep(3000); // 线程睡眠1秒
System.out.println("睡眠后:" + LocalTime.now());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
上述代码中,演示了如何在Java中使用Thread.sleep()
方法使当前线程暂停执行一段时间。在这个例子中,线程将暂停执行3000毫秒,也就是3秒。
代码分析
-
程序首先打印出睡眠前的时间,使用
LocalTime.now()
获取当前的本地时间(不包含日期信息)。 -
接着,
Thread.sleep(3000)
调用使当前线程进入睡眠状态,暂停执行3秒钟。在这段时间内,当前线程不会执行任何操作,并且不会占用CPU资源。 -
如果在
sleep
期间线程被中断,InterruptedException
将被抛出。在catch
块中处理这个异常,打印堆栈跟踪信息。 -
睡眠结束后,程序再次打印出睡眠后的时间。
运行结果
当你运行这个程序时,你会看到控制台输出两次时间,第一次是程序开始睡眠前的时间,第二次是睡眠结束后的时间。两次输出的时间间隔大约是3秒,这由Thread.sleep(3000)
指定。
根据如上测试用例,这里我们本地执行一下,结果展示如下:
注意事项
Thread.sleep()
方法接受的参数是毫秒数,即睡眠的时间长度。InterruptedException
是一个检查型异常,必须显式处理(try-catch)或声明抛出(throws)。- 在多线程程序中,
sleep()
方法是一种简单的线程调度机制,但不是最精确或最可靠的方法。例如,线程可能会比预期的睡眠时间稍微长一些才被唤醒,这取决于操作系统的线程调度策略。 - 为了避免在睡眠期间占用CPU资源,可以在调用
sleep()
之前将线程的状态设置为Thread.State.WAITING
。
这个简单的例子展示了Thread.sleep()
方法的基本用法,它是Java多线程编程中常用的工具之一。在实际应用中,sleep()
方法可以用来实现线程的延迟执行、时间间隔控制等。
案例2:
线程的唤醒可以通过调用线程的notify()
和notifyAll()
方法来实现。下面是一个示例:
package com.example.javase.ms.threadDemo.day3;
/**
* @Author ms
* @Date 2024-04-12 19:42
*/
public class MyThread_wait extends Thread {
private boolean running = false;
public void run() {
synchronized (this) {
while (!running) {
try {
wait(); // 线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public synchronized void wakeup() {
running = true;
notify(); // 唤醒线程
}
public static void main(String[] args) {
MyThread_wait thread = new MyThread_wait();
thread.start();
thread.wakeup();
}
}
对于这段代码,我是展示了一个使用wait()
和notify()
方法来实现线程间的协作的简单示例。在这个例子中,有一个MyThread_wait
类,它继承自Thread
类,并实现了一个简单的启动-通知模式。
代码分析
-
MyThread_wait
类中定义了一个布尔字段running
,用来控制线程的执行状态。 -
在
run()
方法中,线程进入一个while
循环,检查running
变量的状态。如果running
为false
,则线程调用wait()
方法进入等待状态。 -
wait()
方法会导致线程在没有其他线程调用notify()
或notifyAll()
方法之前一直等待。当线程被唤醒时,它会跳过wait()
调用后面的代码,直接继续执行循环。 -
wakeup()
方法用来设置running
为true
,并通过调用notify()
唤醒在此对象监视器上等待的单个线程。如果有多个线程在等待,notify()
方法会选择其中一个线程进行唤醒。 -
在
main
方法中,创建了MyThread_wait
的一个实例,并启动了线程。然后立即调用wakeup()
方法来唤醒正在等待的线程。
运行结果
当程序运行时,MyThread_wait
线程开始执行,但由于running
变量初始为false
,它会在run()
方法中调用wait()
并进入等待状态。随后,main
方法中的wakeup()
调用会唤醒等待的线程。线程被唤醒后,running
变量被设置为true
,线程将继续执行run()
方法中的剩余代码。
根据如上测试用例,这里我们本地执行一下,结果展示如下:
注意事项
wait()
方法必须在synchronized
块或方法中调用,否则会抛出IllegalMonitorStateException
异常。wait()
方法会导致当前线程释放对象的锁,并进入等待池等待其他线程的notify()
或notifyAll()
调用。notify()
方法会随机选择一个在对象监视器上等待的线程进行唤醒,而notifyAll()
会唤醒所有等待的线程。- 在使用
wait()
和notify()
进行线程协作时,要特别注意避免死锁和活锁的情况。
这个示例演示了线程间的基本协作机制,是多线程编程中的一个常见模式。通过这种方式,线程可以根据条件等待或发出通知,以实现更复杂的并发控制逻辑。
线程的等待和通知
线程的等待可以通过调用对象的wait方法实现。下面是一个示例:
/**
* @Author ms
* @Date 2024-04-12 19:42
*/
public class MyThread extends Thread {
public void run() {
synchronized (this) {
try {
wait(); // 线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
synchronized (thread) {
thread.notify(); // 唤醒线程
}
}
}
上述代码中,我们在MyThread类的run()
方法中使用synchronized
关键字来实现线程的同步,然后调用wait方法使线程等待。在Main类中创建MyThread对象,并使用synchronized关键字来实现线程的同步,然后调用notify方法来唤醒线程。
应用场景案例
Java多线程的线程控制技术可以应用于各种场景,下面是一个简单的案例:
假设我们有一个多线程程序,其中有一个线程负责读取网络数据,另一个线程负责处理网络数据。我们希望当读取线程读取到数据后,通知处理线程进行处理。
我们可以使用线程的等待和通知技术来实现这个功能。在读取线程中,可以使用wait方法使线程等待,而在处理线程中,可以使用notify方法来唤醒线程。
/**
* @Author ms
* @Date 2024-04-12 19:42
*/
public class ReadThread extends Thread {
public void run() {
// 读取网络数据
// ...
synchronized (this) {
notify(); // 唤醒处理线程
}
}
}
public class ProcessThread extends Thread {
public void run() {
synchronized (readThread) {
try {
readThread.wait(); // 线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 处理网络数据
// ...
}
private ReadThread readThread; // 读取线程
public ProcessThread(ReadThread readThread) {
this.readThread = readThread;
}
}
public class Main {
public static void main(String[] args) {
ReadThread readThread = new ReadThread();
ProcessThread processThread = new ProcessThread(readThread);
readThread.start();
processThread.start();
}
}
上述代码中,我们定义了一个ReadThread
类和一个ProcessThread
类,分别用于读取网络数据和处理网络数据。在Main类中创建ReadThread
对象和ProcessThread
对象,并通过构造函数将它们关联起来。
优缺点分析
Java多线程的线程控制技术有以下优点:
- 提高程序的运行效率:通过多线程可以同时执行多个任务,提高程序的并发性和响应性。
- 灵活性高:Java提供了多种多线程控制技术,开发人员可以根据实际需求选择合适的技术。
- 方便调试和测试:通过合理使用线程控制技术,可以更好地控制线程的执行顺序和时间,方便调试和测试。
但是,Java多线程的线程控制技术也存在一些缺点:
- 复杂性较高:线程控制涉及到线程的创建、启动、睡眠和唤醒、等待和通知等多个方面,对开发人员的编程能力要求较高。
- 容易出错:线程控制涉及到多线程并发执行,存在线程安全和竞争条件等问题,容易出现错误。
类代码方法介绍
Thread类
start()
:启动线程,使线start()
:启动线程,使线程开始执行run()
方法中的代码。run()
:定义线程执行的任务,必须在run()
方法中编写线程要执行的代码。sleep(long millis)
:使当前线程暂停执行指定的时间(毫秒),并让出CPU给其他线程。interrupt()
:请求中断一个线程,线程可以检查自身的中断状态,并根据需要自行处理中断。isAlive()
:测试线程是否在活动状态,即线程已经开始且尚未完成。join()
:使当前线程等待,直到另一个线程完成执行。notify()
:唤醒在此对象监视器上等待的单个线程。notifyAll()
:唤醒在此对象监视器上等待的所有线程。
Object类(所有类都继承自Object)
wait()
:使当前线程等待,直到另一个线程调用此对象的notify()
或notifyAll()
方法。notify()
:唤醒在此对象监视器上等待的单个线程。notifyAll()
:唤醒在此对象监视器上等待的所有线程。
全文小结
本文详细介绍了Java多线程中的线程控制技术,包括线程的创建和启动、线程的睡眠和唤醒、线程的等待和通知等。通过这些技术,我们可以创建和管理多个线程,使得程序能够并行执行多个任务,从而提高程序的运行效率和响应性。
同时,我们也讨论了多线程编程的优点和缺点,以及如何使用Java提供的Thread
类和Object
类中的方法来控制线程的行为。掌握了这些知识,Java开发人员可以更加灵活和高效地处理多线程程序中的并发问题。
总结
多线程是Java编程中的一个重要特性,它为我们提供了强大的并发处理能力。通过合理地使用线程控制技术,我们可以使程序更加高效和响应迅速。然而,多线程编程也带来了复杂性和潜在的错误,如线程安全问题和竞争条件。因此,开发人员需要仔细设计和测试多线程程序,确保程序的正确性和稳定性。
在实际开发中,除了本文介绍的基础知识,还有许多高级的并发工具和框架,如java.util.concurrent
包中的类,可以帮助我们更好地处理并发问题。不断学习和实践,深入理解多线程和并发编程的原理和技巧,对于成为一名优秀的Java开发人员至关重要。
… …
文末
好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。
… …
学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!
wished for you successed !!!
⭐️若喜欢我,就请关注我叭。
⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。