`
qindongliang1922
  • 浏览: 2196548 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
7265517b-f87e-3137-b62c-5c6e30e26109
证道Lucene4
浏览量:117896
097be4a0-491e-39c0-89ff-3456fadf8262
证道Hadoop
浏览量:126300
41c37529-f6d8-32e4-8563-3b42b2712a50
证道shell编程
浏览量:60263
43832365-bc15-3f5d-b3cd-c9161722a70c
ELK修真
浏览量:71614
社区版块
存档分类
最新评论

多个线程如何轮流打印ABC特定的次数?

    博客分类:
  • JAVA
阅读更多

之前的一篇文章,我给出了关于多线程应用的几个例子:

都是基于Java里面Lock锁实现的,分别是:

(1)两个线程轮流打印奇数和偶数

(2)多个线程模拟买票

(3)模拟生产者消费者


今天再抛砖引玉,思考一下如何在多个线程中,轮流打印特定顺序的信息多少次。

这类问题其实并不难,只要掌握了Java里面线程协作和锁的知识,就可以轻而易举的搞定:

根据这些,我们来假设一个场景,使用三个线程轮流打印ABC字符串3次。

解决思路:

首先需要声明3个线程,我们可以分别叫A线程,B线程,C线程:

在这里面:

A线程仅仅负责打印A。

B线程仅仅负责打印B。

C线程仅仅负责打印C。

但是呢,他们必须是有顺序,也就是说A打印完之后,才能打印B,B打印完后才行打印C,这就涉及线程协作和通信的知识了,A线程打印完毕之后,要通知B线程打印,B线程打印完之后要通知C线程打印,如果有多轮的话,C线程打印完毕之后,还要通知A线程。以及控制多轮次数的终结,不能让程序陷入死循环之中。

在仔细理一下:

(1)首先三个线程启动后,一定是A线程先打印。如果是其他线程先启动,则必须等待,线程间的通信,我们用共享变量来解决。(本质是通过共享内存)

(2)A运行的时候,B和C都在等待

(3)B运行的时候,A和C都在等待

(4)C运行的时候,A和B都在等待

(5)A运行结束通知B运行

(6)B运行结束通知C运行

(7)C运行结束通知A运行

(8)同时,如果要控制几轮打印,则需要在运行时控制循环次数,因为C线程是每一轮的结束标志,循环次数的加和要在C线程里面做。


ok,主要的逻辑基本理清了,我们看下如何用代码实现,先看核心的类:

定义了共享的监视器对象,计数器,共享变量,然后定义了三个方法分别负责打印A,B,C,功能的实现主要用了synchronized + 监视器的wait,notifyAll方法。


```
     static class PrintABC{

         final  Object monitor=new Object();
         volatile  int count=1;//轮次计数,从1开始,为了保证可见性,这里需要用volatile修饰
         String id="A";//贡献的
         int printCount ;

         public PrintABC(int printCount) {
             this.printCount = printCount;
         }

         public void printA() throws InterruptedException {
             while (count < printCount) {
                 synchronized (monitor) {
                     while (!id.equals("A")) {
                         monitor.wait();
                     }
                     System.out.println(Thread.currentThread().getName() + "打印: " + id);
                     id = "B";
                     monitor.notifyAll();
                 }

             }
         }

             public void printB() throws InterruptedException {
                 while (count < printCount) {
                     synchronized (monitor) {
                         while (!id.equals("B")) {
                             monitor.wait();
                         }
                         System.out.println(Thread.currentThread().getName() + "打印: " + id);
                         id = "C";
                         monitor.notifyAll();
                     }

                 }
             }

         public void printC() throws InterruptedException {
             while (count < printCount +1) {//最后一次终结线程,需要多加一次
                 synchronized (monitor) {
                     while (!id.equals("C")) {
                         monitor.wait();
                     }
                     System.out.println(Thread.currentThread().getName() + "打印: " + id+"\n");
                     id = "A";
                     count=count+1;
                     monitor.notifyAll();
                 }

             }
         }

    }
```


然后,我们看下,main方法如何编写:


```
    public static void main(String[] args) {

        PrintABC printABC=new PrintABC(3);

        Thread t1=new Thread(()->{
            try {
                printABC.printA();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });
        t1.setName("A线程");


        Thread t2=new Thread(()->{
                try {
                    printABC.printB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        });
        t2.setName("B线程");

        Thread t3=new Thread(()->{
                try {
                    printABC.printC();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        });
        t3.setName("C线程");

        t2.start();
        t3.start();
        t1.start();



    }
```



这里我们设置了3,也就是仅仅打印3轮,就终止程序。结果如下:


```java
A线程打印: A
B线程打印: B
C线程打印: C

A线程打印: A
B线程打印: B
C线程打印: C

A线程打印: A
B线程打印: B
C线程打印: C
```


至此,这个问题就搞定了,我们思考下,因为这里面采用的是Java的内置锁synchronized来实现的,synchronized关键词虽然使用起来非常简单,但是由于它出现的早,本身也有一些缺点,细心的朋友可能已经发现,上面的通信代码处用的是:

```
monitor.notifyAll();
```

注意这个地方,明明我们只需要唤醒一个线程,为什么需要notifyAll()而不是用notify(),这么做的主要目的是因为synchronized的监视器唤醒的线程是随机的,没办法精确到某个线程,所以它必须唤醒所有的线程,然后重新参与锁的竞争,这样就导致部分线程调度没必要的被交换了一次。

这个地方恰内置锁synchronized的一个弊端,这也是为什么在jdk5之后引入的Lock这样高级锁接口,其相比synchronized在加锁的时候,主要优点是:

(1)提供了公平和非公平调度

(2)可中断

(3)可提供非阻塞

(4)可超时

(5)提供了Condition更细粒度的,锁唤醒条件队列



本文中的例子,完全可以用Lock接口+Condition来达到更细粒度的锁控制,也就是A线程执行完之后,仅仅只唤醒B线程,没有必要把C线程也唤醒,感兴趣的朋友可以思考下怎么实现。






有什么问题可以扫码关注微信公众号:我是攻城师(woshigcs) 关注公众号的朋友,可以加入我们的:攻城师互助交流群,一起学习!







0
0
分享到:
评论

相关推荐

    Java实现多线程轮流打印1-100的数字操作

    本文将详细讲解如何使用Java实现多线程轮流打印1-100的数字,以及在此过程中遇到的并发问题及其解决方案。 首先,我们要理解基本的Java线程创建和运行方式。在示例代码中,通过`Thread`类和`Runnable`接口创建了多...

    C++11用两个线程轮流打印整数的实现方法

    本文将详细解析如何使用C++11的标准线程语法来实现两个线程轮流打印整数,其中一个线程打印奇数,另一个线程打印偶数,同时涉及到线程同步的关键技术——互斥量和条件变量。 首先,为了确保线程安全,我们需要使用...

    用C#实现的多线程同步打印文章(windows操作系统实验)

    同步是多线程编程中的关键概念,它确保在特定时刻只有一个线程可以访问共享资源,以防止数据竞争和不一致。在我们的实验中,缓冲区就是共享资源,需要进行同步控制。C#提供了多种同步机制,如锁(`lock`关键字)、...

    多线程调试打印日志类

    "多线程调试打印日志类"是一个专门设计用于在多线程环境中记录和打印日志的C++类。此类实现了一个单例模式,确保在整个应用程序中只有一个实例存在,从而避免了资源竞争和日志混乱的问题。 首先,单例模式是一种...

    c#多线程顺序打印1-100数字-源码.rar

    本资源"ConsoleApp1"是一个C#实现的多线程示例,它演示了如何在多线程环境中顺序打印1到100的数字。在多线程环境下实现顺序打印看似简单,实则涉及到线程同步和控制的问题,这通常通过各种同步原语来实现。在这个...

    文件打印问题 (c++多线程实现)

    在处理文件打印这样的任务时,多线程可以并行地读取、处理和打印多个文件,从而提高效率。 在“文件打印问题”中,可能会遇到的一个关键问题是资源竞争。当多个线程尝试同时访问和修改同一个文件时,如果没有适当的...

    多线程依次循环打印26个英文字母

    在这个"多线程依次循环打印26个英文字母"的问题中,我们主要关注如何在多个线程之间同步操作,确保字母按照顺序正确地输出。这里涉及了`条件队列`和`lock`两个关键概念。 首先,让我们理解一下`多线程`。在单核...

    向量在同一个块中用多个线程执行

    在CUDA编程中,"向量在同一个块中用多个线程执行"是一种并行计算策略,主要用于提升GPU的计算效率。CUDA(Compute Unified Device Architecture)是NVIDIA推出的一种编程模型,它允许程序员直接利用GPU的强大计算...

    log4j2异步多线程打印

    在`asnc-print-different-logfile`这个压缩包中,可能包含了一个示例项目,用于演示如何配置和使用Log4j2实现异步多线程打印。项目可能包含以下元素: 1. `pom.xml`:Maven项目的配置文件,定义了依赖项和构建指令...

    java代码-三个线程轮流输出abc

    本示例中的"java代码-三个线程轮流输出abc"是一个经典的多线程同步问题,通常被称为"生产者消费者问题"的一个变种。在这个场景下,三个线程分别负责输出字符'a'、'b'和'c',它们需要轮流进行,以保证输出的顺序为abc...

    socket 一个服务器对应多个客户端使用多线程

    在标题"socket 一个服务器对应多个客户端使用多线程"中,提到的是使用Socket编程来设计一个服务器,该服务器能够同时处理来自多个客户端的连接请求,这通常被称为“一对一”或“一对多”模型。在Linux环境下,这种...

    三个线程交错输出

    每个线程会有一个特定的打印序列,例如线程A打印"ABC",线程B打印"DEF",线程C打印"GHI"。为了实现交错输出,我们需要控制线程的执行顺序,确保它们在特定时刻能够交替访问共享资源(在这种情况下,共享资源是打印...

    C#多线程实现调用外部程序并获取打印结果

    一个简单的例子,C#多线程实现调用外部程序并获取打印结果 一个简单的例子,C#多线程实现调用外部程序并获取打印结果 一个简单的例子,C#多线程实现调用外部程序并获取打印结果 一个简单的例子,C#多线程实现调用...

    四个线程写四个文件abcd

    四个线程,线程A只能写a,线程B只能写b,线程C只能写c,线程D只能写d,四个文件a.bin b.bin c.bin d.bin内容要求分别是:abcdabcd...;bcdabcda...;cdabcdab...;dabcdabc....; 如何实现四个线程写上面四个文件?

    3个线程,根据不同规则打印线程名称

    这个示例展示了如何使用Java的多线程特性以及同步机制来实现特定的打印顺序。然而,正如描述中所指出的,频繁的`wait()`和`notifyAll()`操作可能会造成CPU利用率较高,这是多线程同步的一个常见问题。在实际应用中,...

    juc多线程交替打印ABC、熟悉lock的唤醒机制

    现场交替访问、等待和唤醒

    .NET多线程实例

    总之,这个.NET 2.0的多线程实例涵盖了多线程的创建与管理、线程同步、UI更新、文件系统操作、异步编程等多个核心知识点,对于理解和实践.NET环境下的并发编程具有很高的价值。通过学习和实践这些示例,开发者可以...

    C# 高效线程安全,解决多线程写txt日志类.zip

    "C# 高效线程安全,解决多线程写txt日志类.zip" 提供了一个专门用于多线程环境下写入txt日志文件的解决方案,确保了在并发写入时的数据一致性与程序稳定性。 首先,我们要理解什么是线程安全。线程安全是指当多个...

    多线程操作串口示例(带modbus

    多线程意味着一个程序内可以同时运行多个线程,这样就可以并发处理不同的任务,提高程序的响应速度和资源利用率。在处理串口通信时,可能需要同时监听来自不同设备的数据,或者同时发送命令到多个设备,此时多线程的...

Global site tag (gtag.js) - Google Analytics