Java多线程中顺序打印

简介: Java多线程中顺序打印

阿里非典型程序员一枚 ,记录平平无奇程序员在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法(公众号同名

题目

在多个线程中循环打印特定字符并保持顺序的三种方法

在Java多线程编程中,有时候我们需要多个线程按照特定的顺序执行某些任务,比如循环打印特定的字符。这通常要求线程间进行协调,以确保任务按照预期的顺序执行。下面我们将介绍三种实现这一功能的方法。

在多个线程中循环打印特定字符并保持顺序的三种方法

在Java多线程编程中,有时候我们需要多个线程按照特定的顺序执行某些任务,比如循环打印特定的字符。这通常要求线程间进行协调,以确保任务按照预期的顺序执行。下面我们将介绍三种实现这一功能的方法。

方法一:synchronized(配合volatile关键字)

volatile关键字可以确保多线程环境中变量的可见性,但它并不保证原子性。因此,在使用volatile时,我们需要结合其他同步机制来实现顺序控制。

public class VolatilePrinting {  
    private volatile int currentThreadIndex = 0;  
    private final int numThreads = 3;  
    private final char[] chars = {'A', 'B', 'C'};  
  
    public static void main(String[] args) {  
        VolatilePrinting volatilePrinting = new VolatilePrinting();  
        Thread[] threads = new Thread[volatilePrinting.numThreads];  
        for (int i = 0; i < volatilePrinting.numThreads; i++) {  
            final int threadIndex = i;  
            threads[i] = new Thread(() -> {  
                while (true) {  
                    synchronized (volatilePrinting) {  
                        while (volatilePrinting.currentThreadIndex % volatilePrinting.numThreads != threadIndex) {  
                            try {  
                                volatilePrinting.wait();  
                            } catch (InterruptedException e) {  
                                Thread.currentThread().interrupt();  
                                return;  
                            }  
                        }  
                        System.out.print(volatilePrinting.chars[threadIndex]);  
                        volatilePrinting.currentThreadIndex++;  
                        if (volatilePrinting.currentThreadIndex >= 100) { // 假设打印100个字符后结束  
                            break;  
                        }  
                        volatilePrinting.notifyAll();  
                    }  
                }  
            });  
        }  
        for (Thread thread : threads) {  
            thread.start();  
        }  
    }  
}

在这个例子中,我们使用了一个volatile变量currentThreadIndex来跟踪当前应该打印哪个字符的线程。每个线程在打印字符之前会检查这个变量,如果不是它的索引,则等待;如果是它的索引,则打印字符并增加currentThreadIndex的值,然后唤醒所有等待的线程。这样,线程们就会按照顺序打印字符。

方法二:使用Lock和Condition

Lock和Condition提供了更灵活的同步机制,可以实现更复杂的线程间协作。

import java.util.concurrent.locks.Condition;  
import java.util.concurrent.locks.Lock;  
import java.util.concurrent.locks.ReentrantLock;  
  
public class LockPrinting {  
    private final Lock lock = new ReentrantLock();  
    private final Condition[] conditions = new Condition[3];  
    private int currentThreadIndex = 0;  
    private final char[] chars = {'A', 'B', 'C'};  
  
    public LockPrinting() {  
        for (int i = 0; i < conditions.length; i++) {  
            conditions[i] = lock.newCondition();  
        }  
    }  
  
    public static void main(String[] args) {  
        LockPrinting lockPrinting = new LockPrinting();  
        Thread[] threads = new Thread[3];  
        for (int i = 0; i < 3; i++) {  
            final int threadIndex = i;  
            threads[i] = new Thread(() -> {  
                while (true) {  
                    lockPrinting.lock.lock();  
                    try {  
                        while (lockPrinting.currentThreadIndex % 3 != threadIndex) {  
                            conditions[threadIndex].await();  
                        }  
                        System.out.print(lockPrinting.chars[threadIndex]);  
                        lockPrinting.currentThreadIndex++;  
                        if (lockPrinting.currentThreadIndex >= 100) { // 假设打印100个字符后结束  
                            break;  
                        }  
                        conditions[(threadIndex + 1) % 3].signal();  
                    } catch (InterruptedException e) {  
                        Thread.currentThread().interrupt();  
                        return;  
                    } finally {  
                        lockPrinting.lock.unlock();  
                    }  
                }  
            });  
        }  
        for (Thread thread : threads) {  
            thread.start();  
        }  
    }  
}

在这个例子中,我们为每个线程创建了一个Condition对象,用于等待和通知。当线程需要打印字符时,它会检查currentThreadIndex的值,如果不是它的索引,则调用await()方法等待;如果是它的索引,则打印字符

方法三:使用Semaphore

import java.util.concurrent.Semaphore;
class PrintThread implements Runnable {
    private Semaphore currentSemaphore;
    private Semaphore nextSemaphore;
    private String letter;
    private int count;
    public PrintThread(Semaphore currentSemaphore, Semaphore nextSemaphore, String letter, int count) {
        this.currentSemaphore = currentSemaphore;
        this.nextSemaphore = nextSemaphore;
        this.letter = letter;
        this.count = count;
    }
    @Override
    public void run() {
        try {
            for (int i = 0; i < count; i++) {
                currentSemaphore.acquire();
                System.out.print(letter);
                nextSemaphore.release();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Semaphore semaphoreA = new Semaphore(1);
        Semaphore semaphoreB = new Semaphore(0);
        Semaphore semaphoreC = new Semaphore(0);
        int count = 100;
        Thread threadA = new Thread(new PrintThread(semaphoreA, semaphoreB, "A", count));
        Thread threadB = new Thread(new PrintThread(semaphoreB, semaphoreC, "B", count));
        Thread threadC = new Thread(new PrintThread(semaphoreC, semaphoreA, "C", count));
        threadA.start();
        threadB.start();
        threadC.start();
    }
}

说明:

使用Semaphore控制线程的顺序和并发输出。通过创建三个Semaphore对象,每个Semaphore对象对应一个线程。通过acquire()和release()方法来控制线程的获取和释放。初始时,只有线程A可以获取资源;线程A获取后释放并唤醒线程B,这样线程B可以获取资源并释放唤醒线程C;线程C以此类推。通过这种方式,实现了循环和顺序输出字符ABC。

通过以上三种方式,我们可以实现在多线程并发执行的环境下循环且顺序打印"ABC"100次的目标。每种方法都有其独特的实现方式,可以根据具体需求和场景选择适合的方法来实现线程的控制和顺序打印。

总结

方法 特点
使用volatile关键字 适用于简单的顺序控制,代码简单,但不适用于复杂的线程同步和互斥情况。
使用Lock和Condition 提供了更丰富的线程同步和互斥机制,并可以精确控制线程的等待和唤醒,适用于复杂的线程交互情况。
使用Semaphore 通过信号量来控制线程的获取和释放,并可以灵活地控制线程的顺序,适用于多种线程同步场景。
相关文章
|
13天前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
48 0
|
25天前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
59 16
|
2月前
|
缓存 并行计算 安全
关于Java多线程详解
本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。
|
2月前
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践
|
3月前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
300 83
|
3月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
139 0
|
3月前
|
存储 Java 调度
Java虚拟线程:轻量级并发的革命性突破
Java虚拟线程:轻量级并发的革命性突破
251 83
|
4月前
|
移动开发 Java
说一说 Java 是如何实现线程间通信
我是小假 期待与你的下一次相遇 ~