Java 在2个线程中交替输出数字和字母,不带锁

Java 在2个线程中交替输出数字和字母,不带锁,java,multithreading,concurrency,Java,Multithreading,Concurrency,在一次采访中,我被要求使用两个而且只有两个线程来打印字符串1A2B3C…24X25Y26Z,其中一个线程只打印数字,另一个线程只打印字母。我编写了以下代码: 公共类应用程序{ 静态易失性int x=0; 静态易失性布尔numPrinted=false; 静态类NumThread实现Runnable{ @凌驾 公开募捐{ 而(x代码>它们自己的()堆栈,并通过单个共享的volatile boolean进行同步。这就是你的意思吗?@CedricSun这是一种方法。另一种方法是将计数器的范围扩大一倍,

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

公共类应用程序{
静态易失性int x=0;
静态易失性布尔numPrinted=false;
静态类NumThread实现Runnable{
@凌驾
公开募捐{
而(x<26){
如果(!numPrinted){
系统输出打印(x+1);
numPrinted=true;
}
}
}
}
静态类LetterThread实现可运行{
@凌驾
公开募捐{
而(x<26){
如果(numPrinted){
系统输出打印((字符)('A'+x));
++x;
numPrinted=false;
}
}
}
}
公共静态void main(字符串[]args){
Thread foo=新线程(new NumThread());
螺纹杆=新螺纹(新的LetterThread());
foo.start();
bar.start();
试一试{
foo.join();
bar.join();
}捕获(InterruptedException被忽略){
}
System.out.println();
}
}
在10次运行中,大约有1次将产生

1A2B3C4D5E6F7G8H9I10J11K12L13M14N15O16P17Q18R19S20T21U22V23W24X25Y26Z27

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

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

  • 凸纹印花

  • NumThread检查您的代码在两个类成员x和numPrinted上是否有竞争条件。 您可以尝试实现互斥和条件变量。 下面是一个每次都能正常工作的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;”命令显式指示

    程序的输出为:


    1A2B3C4D5E6F7G8H9I10J11K12L13M14N15O16P17Q18R19S20T21U2V23W24X25Y26Z

    仅使用一个共享变量(在本例中为
    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;
                }
            }
        }
    
    
    static volatile boolean numPrinted=false;
    静态类NumThread实现Runnable{
    @凌驾
    公开募捐{
    
    对于(int=1;OOP数,我忘记了主线)…但是我们还是坚持下去。你想说“在NothToint”中吗?比如,<代码> >(NoMeNox&&席),改变逻辑只使用一个变量就简单了。@霍格尔,我可以看到每个线程如何依赖他们的内部计数器<代码> x>代码>它们自己的<代码>()
    堆栈,并通过单个共享的
    volatile boolean
    进行同步。这就是你的意思吗?@CedricSun这是一种方法。另一种方法是将计数器的范围扩大一倍,让一个线程等待偶数值,另一个线程等待奇数值(每个线程轮到它时前进一个,并使用计数器/2进行打印).任何一种方法都应该有效。