大家好,我是香香。
昨天我们讲解了多线程的基本概念以及使用多线程的好处,今天就来通过代码的方式了解一下多线程中的核心概念:线程创建
、线程同步
和线程通信
。以及多线程中常用方法
的介绍!
多线程
1. 多线程编程的原理
多线程编程的核心原理是将一个程序分成多个独立的子任务,并在不同的线程上并发地执行这些子任务。通过充分利用计算机的多核处理器,提高程序的运行效率和并发性。多线程编程涉及到线程的创建、生命周期管理、线程同步和线程通信等概念。
2. 多线程编程的常用技术
①创建线程:
这个其实上一章有讲到过,那就再复习一遍吧~
通过继承 Thread 类或实现 Runnable 接口来创建线程。继承 Thread 类需要重写 run() 方法,实现 Runnable 接口需要实现 run() 方法,并通过 Thread 类的构造函数创建线程对象。
-
继承 Thread 类:
/**
@Author: Coderxinang
**/
class MyThread extends Thread {
public void run() {
// 定义线程的任务逻辑
// ...
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // 启动线程
}
}
-
实现 Runnable 接口:
/**
@Author: Coderxinang
**/
class MyRunnable implements Runnable {
public void run() {
// 定义线程的任务逻辑
// ...
}
}
public class Main {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start(); // 启动线程
}
}
②线程同步:
线程同步是保证多个线程按照一定的顺序安全地访问共享资源的机制。常用的线程同步技术包括使用 synchronized 关键字对代码块或方法进行同步,以及使用 Lock 接口和 Condition 接口实现显式锁机制。
/**
@Author: Coderxinang
**/
public class ThreadSynchronizationExample {
private static int count = 0; // 共享的计数变量
private static final Object lock = new Object(); // 定义一个共享的锁对象
/**
* 使用 synchronized 关键字对 increment() 方法进行加锁,确保原子性操作
*/
public static void increment() {
synchronized (lock) { // 使用 synchronized 关键字加锁
count++; // 对 count 变量进行递增操作
}
}
/**
* 主函数,创建两个线程并启动,最后输出 count 的值
*/
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
increment(); // 调用 increment() 方法对 count 变量进行递增操作
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
increment(); // 调用 increment() 方法对 count 变量进行递增操作
}
});
t1.start(); // 启动线程 t1
t2.start(); // 启动线程 t2
t1.join(); // 等待线程 t1 执行完毕
t2.join(); // 等待线程 t2 执行
③线程通信:
线程通信是多个线程间传递信息和协调工作的机制。常用的线程通信技术包括使用 wait()、notify() 和 notifyAll() 方法配合 synchronized 关键字实现等待/通知机制,以及使用 BlockingQueue 阻塞队列来实现线程间的数据交换。
import java.util.LinkedList;
import java.util.Queue;
/**
* 本示例演示了如何使用线程通信技术,如wait()、notify()和notifyAll(),以及synchronized关键字来实现等待/通知机制。
*
* 在本示例中,我们有一个生产者线程和一个消费者线程。生产者线程向共享队列中添加元素,消费者线程从队列中消费元素。
* 如果队列已满,生产者线程将进入等待状态,直到队列有空间可用。
* 如果队列为空,消费者线程将进入等待状态,直到队列中有元素可供消费。
* 等待/通知机制确保生产者和消费者线程能够顺利地协作。
*/
public class ThreadCommunicationExample {
private static final int MAX_SIZE = 5; // 共享队列的最大容量
private static Queue<String> queue = new LinkedList<>(); // 共享队列
/**
* 生产者线程,向队列中添加元素
*/
static class Producer extends Thread {
@Override
public void run() {
synchronized (queue) {
while (queue.size() == MAX_SIZE) {
try {
System.out.println("队列已满,生产者正在等待...");
queue.wait(); // 等待直到队列有空间可用
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String item = "物品 " + System.currentTimeMillis();
queue.add(item);
System.out.println("生产者生产了:" + item);
queue.notifyAll(); // 通知消费者线程有新的元素可供消费
}
}
}
/**
* 消费者线程,从队列中消费元素
*/
static class Consumer extends Thread {
@Override
public void run() {
synchronized (queue) {
while (queue.isEmpty()) {
try {
System.out.println("队列为空,消费者正在等待...");
queue.wait(); // 等待直到队列中有元素可供消费
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String item = queue.poll();
System.out.println("消费者消费了:" + item);
queue.notifyAll(); // 通知生产者线程有空间可供生产
}
}
}
/**
* 主函数,创建一个生产者线程和一个消费者线程,并启动它们。
*/
public static void main(String[] args) {
Producer producer = new Producer();
Consumer consumer = new Consumer();
producer.start(); // 启动生产者线程
consumer.start(); // 启动消费者线程
}
}
输出结果:
队列为空,消费者正在等待...
队列已满,生产者正在等待...
生产者生产了:物品 1640479588969
消费者消费了:物品 1640479588969
3. 多线程编程的常用方法
以下是多线程编程的常用方法的讲解:
-
wait()
: 在一个对象上调用该方法会使当前线程进入等待状态,同时释放对象上的锁。该方法通常与notify()
或notifyAll()
配合使用,用于实现线程之间的等待/通知机制。 -
notify()
: 在一个对象上调用该方法会唤醒正在等待该对象的某个线程。如果有多个线程等待该对象,则只会唤醒其中一个线程,具体唤醒哪个线程由系统决定。 -
wait() 和 notify()
:配合 synchronized 关键字使用,实现线程的等待和唤醒机制。wait() 方法使当前线程进入等待状态,直到被其他线程调用 notify() 方法唤醒。适用于线程间需要协调处理的情况。 -
notifyAll()
: 在一个对象上调用该方法会唤醒正在等待该对象的所有线程。所有被唤醒的线程将竞争对象的锁。 -
sleep()
: 当前线程会暂停执行指定的时间,以毫秒为单位。在这段时间内,线程不会释放任何资源,包括锁。一旦睡眠时间结束,线程将恢复执行。适用于需要暂停执行一段时间的情况,如定时任务。 -
start()
: 调用线程的start()
方法会启动线程,使其进入就绪状态,并自动调用线程的run()
方法。在start()
方法被调用后,线程的生命周期由JVM来管理。 -
join()
: 等待线程终止。调用线程对象的 join() 方法会使当前线程进入等待状态,直到被调用的线程执行完毕后才继续执行。适用于需要等待其他线程执行完毕后再进行后续操作的情况。一般适用于父线程等待子线程执行完毕的场景。 -
yield()
: 当前线程调用yield()
方法会提示调度器将CPU资源让给其他线程。该方法会暂停当前线程的执行,并允许其他线程有机会执行。但是,并不保证yield()
方法一定会导致线程切换。适用于希望让其他具有相同优先级的线程有机会执行的情况(当前线程愿意让出 CPU 的执行时间,但是不一定会成功让出)。
这些方法是多线程编程中常用的方法,可以帮助我们实现线程之间的协作和资源控制。当使用这些方法时,需要「注意线程的同步和互斥」,以「避免产生竞态条件或死锁」等问题。
注:仅作者个人知识分享,如有错误可指正!
原文始发于微信公众号(Coder香):【每日一题】Java 基础篇 – 多线程专栏二:深入探索多线程编程之原理、技术与常用方法详解
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/217613.html