当前位置: 首页 > 知识库问答 >
问题:

2线程无锁交替输出数字和字母

濮阳和泰
2023-03-14

在一次采访中,我被要求使用两个而且只有两个线程来打印字符串1A2B3C。。。24X25Y26Z,其中一条线只打印数字,另一条线只打印字母。我编写了以下代码:

public class App {
    static volatile int x = 0;
    static volatile boolean numPrinted = false;

    static class NumThread implements Runnable {
        @Override
        public void run() {
            while (x < 26) {
                if (!numPrinted) {
                    System.out.print(x + 1);
                    numPrinted = true;
                }
            }
        }
    }

    static class LetterThread implements Runnable {
        @Override
        public void run() {
            while (x < 26) {
                if (numPrinted) {
                    System.out.print((char) ('A' + x));
                    ++x;
                    numPrinted = false;
                }
            }
        }
    }


    public static void main(String[] args) {
        Thread foo = new Thread(new NumThread());
        Thread bar = new Thread(new LetterThread());
        foo.start();
        bar.start();
        try {
            foo.join();
            bar.join();
        } catch (InterruptedException ignored) {

        }
        System.out.println();
    }
}

在大约10次运行中有1次,它将产生

1A2B3C4D5E6F7G8H9I10J11K12L13M14N15O16P17Q18R19S20T21U22V23W24X25Y26Z27

我的代码有什么并发问题?我故意避免原子整数。

共有3个答案

叶冥夜
2023-03-14

只需使用一个共享变量(在本例中为numPrinted)就可以更容易地解决这个问题。这表明哪个线程需要执行某些操作。线程本身可以跟踪它们的进度,等到需要打印某个内容时再打印,然后翻转对两个线程都可见的numPrinted变量。这可能看起来像:

    static volatile boolean numPrinted = false;

    static class NumThread implements Runnable {
        @Override
        public void run() {
            for (int number = 1; number <= 26; number++) {
                // Wait until we need to print a number
                while (numPrinted) {
                    Thread.yield(); //optional
                };
                // Print a number
                System.out.print(number);
                // Set flag that we printed a number
                numPrinted = true;
            }
        }
    }

    static class LetterThread implements Runnable {
        @Override
        public void run() {
            for (char letter = 'A'; letter <= 'Z'; letter++) {
                // Wait until we need to print a letter
                while (!numPrinted) {
                    Thread.yield(); //optional
                }
                // Print a letter
                System.out.print(letter);
                //set flag that we printed a letter
                numPrinted = false;
            }
        }
    }

施刚毅
2023-03-14

您的程式码在两个类别成员x和NumPrted上有竞态条件。您可以尝试实作互斥锁和条件变数。以下是每次都能正确运作的Ada范例。

with Ada.Text_IO;         use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure Main is
   protected traffic_Cop is
      entry Nums (Val : Positive);
      entry Alpha (Val : Character);
   private
      gate : Boolean := True;
   end traffic_Cop;

   protected body traffic_cop is
      entry Nums (Val : Positive) when gate is
      begin
         Put (Item => Val, Width => 1);
         gate := False;
      end Nums;
      entry Alpha (Val : Character) when not gate is
      begin
         Put (Val);
         gate := True;
      end Alpha;
   end traffic_cop;

   task printnums;
   task printalpha;

   task body printnums is
   begin
      for value in 1 .. 26 loop
         traffic_cop.Nums (value);
      end loop;
   end printnums;

   task body printalpha is
      subtype chars is Character range 'A' .. 'Z';
   begin
      for value in chars loop
         traffic_Cop.Alpha (value);
      end loop;
   end printalpha;

begin
   null;
end Main;

该示例使用一个受保护对象和两个任务。Ada保护的对象受到保护,不受不适当的种族条件的影响。任务通常映射到操作系统线程。

受保护对象可以公开三种方法

受保护函数隐式地建立并实现共享读锁,允许多个任务同时从受保护对象读取数据。

受保护的过程隐式地建立并实现独占读写锁,一次只允许一个任务无条件地读取或修改受保护对象内的数据。

受保护的条目隐式地建立并实现一个排他的读写锁和一个条目条件。只有当与条目相关联的边界条件计算为True时,调用条目的任务才能读取和/或修改受保护对象的内容。

上面的例子使用了两个条目。

受保护对象traffic_cop只包含一个变量,即名为gate的布尔值,该值被初始化为True。

受保护的主体包含两个条目的实现。条目Nums只能在gate为True时执行。条目Alpha只能在gate为false时执行。

声明了两个任务。

任务printnums循环遍历值1到26。在每个循环迭代中,该任务调用traffic_cop。nums然后打印数值。

任务printalpha循环遍历值“A”到“Z”。在每个循环迭代中,该任务调用traffic_cop。alpha然后打印alpha值。

当达到主过程的“开始”语句时,这两个任务都会自动启动。主过程不做任何事情,这由“null”命令明确指示。

该程序的输出为:

1A2B3C4D5E6F7G8H9I10J11K12L13M14N15O16P17Q18R19S20T212V23W24X25Y26Z

幸乐湛
2023-03-14

两个线程都在忙等待。所以这是可能的:

  • LetterThread打印Z
  • NumThread检查x

在NumThred中,再次检查x之后的NumPrted。

 类似资料:
  • 我试图实现这一点:第一个线程打印1,第二个线程打印2,第三个线程打印3,第一个线程打印4等等: 我做到了这一点,并发挥了作用: 输出是这样的: 它达到了目的,但是如果我有两个线程要打印,那么我必须使用更多的如果条件。 任何人都可以建议以更好的形式编写这段代码,以更干净的方式完成任务,这样如果添加更多线程,它就可以扩展。

  • 我有一个类似的问题,但是我知道当我要求阅读一行时,发件人应该发送一个行尾。 让我困惑的是,在调试中,它是有效的。可能是因为我在调试时跳过的顺序(直到现在我都不知道这会有什么不同),但我想更好地理解它。 我已经使用线程,但不是很多。 这是我的服务器类: 线程(基于此) 和客户: 它似乎在某个地方进入了死锁,出于某种原因,除非在调试中运行,否则永远不要在向客户端发送数据的服务器类上输入该死锁 (顺便说

  • 我想采取两个字符串和交替的字符到一个新的字符串使用for方法。 例如:“两个”和“一个” 结果:“townoe” 这就是我到目前为止所拥有的,我真的不知道如何完成它。

  • 问题内容: 我在Python中有一个字符串 我试图替换以该词作为参数的函数输出开头的每个词。 有聪明的方法吗? 问题答案: 您可以将函数传递给。该函数将接收一个match对象作为参数,用于将匹配提取为字符串。

  • 主要内容:字符串的输出,字符串的输入其实在《 C语言输入输出》一章中我们已经提到了如何输入输出字符串,但是那个时候我们还没有讲解字符串,大家理解的可能不透彻,所以本节我们有必要再深入和细化一下。 字符串的输出 在C语言中,有两个函数可以在控制台(显示器)上输出字符串,它们分别是: puts():输出字符串并自动换行,该函数只能输出字符串。 printf():通过格式控制符输出字符串,不能自动换行。除了字符串,printf() 还能输

  • 问题内容: 我一直在尝试解决涉及使用wait()和notify()的线程通信的问题。基本上我有2个线程T1和T2,我希望它们按以下顺序执行 T1,T2,T1,T2 .....我该如何实现? 实际的问题:有两个线程T1-打印奇数(例如1-100),而T2-打印偶数(1-100)。现在,输出应为1,2,3,4,5,.... 100 问题答案: 您描述了生产者-消费者模式。 它是Java的实现,在许多J