我想使用两个线程打印斐波那契数列。像第一个数字应该由第一个线程打印,然后由第二个线程打印第二个数字,依此类推

时间:2020-12-27 09:15:08

标签: java multithreading parallel-processing

我希望斐波那契数列由线程打印,该系列的第一个数字应由第一个线程打印,然后由第二个线程打印第二个数字,然后由第一个线程打印第三个,依此类推。

我通过使用数组尝试了此代码,例如使用线程打印数组元素,但我无法在线程之间切换。

class Fibonacci{
    void printFibonacci() {
       int fibArray[] = new int[10];
       int a = 0;
       int b = 1;
       fibArray[0] = a;
       fibArray[1] = b;
       int c;
       for(int i=2;i<10;i++) {
           c = a+b;
           fibArray[i] = c;
           a = b;
           b = c;
       }
       for(int i=0;i<10;i++) {
        if(Integer.parseInt(Thread.currentThread().getName())%2==0 && (i%2==0))
        {
            System.out.println("Thread " +Thread.currentThread().getName()+" "+fibArray[i]);
            try{
                wait();
            }catch(Exception e) {}
        }
        else if(Integer.parseInt(Thread.currentThread().getName())%2!=0 && (i%2!=0))
        {
            System.out.println("Thread " +Thread.currentThread().getName()+" "+fibArray[i]);
        }
     }
   }
}

public class FibonacciUsingThread {

    public static void main(String[] args) throws Exception {
        Fibonacci f = new Fibonacci();
        Thread t1 = new Thread(()->
        {
            f.printFibonacci();
        });
        Thread t2 = new Thread(()->
        {
            f.printFibonacci();
        });
        t1.setName("0");
        t2.setName("1");
        t1.start();
        t1.join();
        t2.start();
    }
}

4 个答案:

答案 0 :(得分:3)

代码中的以下行是 causing t1 to finish before t2 can start

t1.join();

除此之外,您还需要在方法 printFibonacci 上进行同步。

你可以这样做:

class Fibonacci {
    synchronized void printFibonacci() throws InterruptedException {
        int fibArray[] = new int[10];
        int a = 0;
        int b = 1;
        fibArray[0] = a;
        fibArray[1] = b;
        int c;
        for (int i = 2; i < 10; i++) {
            c = a + b;
            fibArray[i] = c;
            a = b;
            b = c;
        }
        for (int i = 0; i < 10; i++) {
            String currentThreadName = Thread.currentThread().getName();
            if (currentThreadName.equals("1")) {
                if (i % 2 == 0) {
                    System.out.println("Thread " + Thread.currentThread().getName() + " " + fibArray[i]);
                    notify();
                } else {
                    wait();
                }
            } else if (currentThreadName.equals("0")) {
                if (i % 2 == 1) {
                    System.out.println("Thread " + Thread.currentThread().getName() + " " + fibArray[i]);
                    notify();
                } else {
                    wait();
                }
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {

        Fibonacci f = new Fibonacci();
        Thread t1 = new Thread(() -> {
            try {
                f.printFibonacci();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                f.printFibonacci();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t1.setName("0");
        t2.setName("1");
        t1.start();
        t2.start();
    }
}

输出:

Thread 1 0
Thread 0 1
Thread 1 1
Thread 0 2
Thread 1 3
Thread 0 5
Thread 1 8
Thread 0 13
Thread 1 21
Thread 0 34

答案 1 :(得分:1)

正如“@Live and Let Live”所指出的,在正确性方面,您的代码的主要问题是缺少 synchronized 子句以及在启动第二个线程之前调用第一个线程的 join。< /p>

IMO,您可以通过首先将关注点分开来稍微清理一下代码,即类 Fibonacci 将仅负责计算给定数组的斐波那契数:

class Fibonacci{
    void getFibonacci(int[] fibArray) {
        int a = 0;
        int b = 1;
        fibArray[0] = a;
        fibArray[1] = b;
        int c;
        for(int i=2;i<fibArray.length;i++) {
            c = a+b;
            fibArray[i] = c;
            a = b;
            b = c;
        }
    }
}

通过这种方式,您可以在没有任何线程相关代码的情况下保持 Fibonacci 类的简洁。此外,getFibonacci 现在更加抽象;您可以像以前一样计算超过 10 个元素的 fib

然后在类 FibonacciUsingThread 上:

public class FibonacciUsingThread {

        public static void main(String[] args) throws Exception {
            int [] array_fib = new int[10];
            Fibonacci f = new Fibonacci();
            f.getFibonacci(array_fib);
            Thread t1 = new Thread(()->
            {
                for(int i = 0; i < array_fib.length; i+=2)
                    System.out.println("Thread 1:" + array_fib[i]);
            });
            Thread t2 = new Thread(()->
            {
                for(int i = 1; i < array_fib.length; i+=2)
                    System.out.println("Thread 2:" + array_fib[i]);
            });
            t1.start();
            t2.start();
            t1.join();
            t2.join();
        }
    }

首先,您使用线程计算斐波那契数列,让所有线程计算相同的东西是没有意义的。之后,您指定 Thread 1Thread 2 将分别打印偶数和奇数位置。

除非这只是一个练习线程和同步,否则使用线程来完成这种工作没有多大意义。在您的代码中,值得并行化的部分是斐波那契数本身的计算,而不是打印部分。

之前显示的代码不会按顺序打印斐波那契数列,为此您需要确保线程在遍历数组的每个元素后等待。因此,您需要修改将由线程执行的代码,即:

Thread t1 = new Thread(()->
{
    synchronized (array_fib){
        for(int i = 0; i < array_fib.length; i++)
            if(i % 2 == 0) {
                System.out.println("Thread 1:" + array_fib[i]);
                try {
                    array_fib.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            else
                array_fib.notify();
    }
});
Thread t2 = new Thread(()->
{
    synchronized (array_fib){
        for(int i = 0; i < array_fib.length; i++)
            if(i % 2 != 0) {
                System.out.println("Thread 2:" + array_fib[i]);
                try {
                    array_fib.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            else
                array_fib.notify();
    }
});

我们可以通过提取具有将分配给线程的工作的方法来消除代码冗余。例如:

private static void printFib(String threadName, int[] array_fib, Predicate<Integer> predicate) {
    for (int i = 0; i < array_fib.length; i++)
        if (predicate.test(i)) {
            System.out.println(threadName + " : " + array_fib[i]);
            try { 
                 array_fib.wait();
            } catch (InterruptedException e) {
                // do something about it
            }
        } else
            array_fib.notify();
}

和主要代码:

public static void main(String[] args) throws Exception{
    int [] array_fib = new int[10];
    Fibonacci f = new Fibonacci();
    f.getFibonacci(array_fib);
    Thread t1 = new Thread(()-> {
        synchronized (array_fib){
            printFib("Thread 1:", array_fib, i1 -> i1 % 2 == 0);
        }
    });
    Thread t2 = new Thread(()-> {
        synchronized (array_fib){
            printFib("Thread 2:", array_fib, i1 -> i1 % 2 != 0);
        }
    });
    t1.start();
    t2.start();
    t1.join();
    t2.join();
}

答案 2 :(得分:0)

除了已经说过和已经回答的所有内容之外,我只想为 Fibonacci 序列实现添加一种替代方法,无需数组和预先标注:

public class Fibonacci {

    private int index = -1;

    private int previous = 0;
    private int last = 1;

    synchronized public int getNext() {

      index++;

      if( index == 0 ) return previous;
      if( index == 1 ) return last;

      int next = last + previous;
      if( next < 0 ) throw new ArithmeticException( "integer overflow" );

      previous = last;
      last = next;

      return next;
    }

}

仅受数字数据类型溢出的限制,在本例中为整数。

答案 3 :(得分:0)

作为替代方案,您可以使用公平的 Semaphore 在线程之间交替,并使用 AtomicReference 来保持共享状态。举个例子:

import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicReference;

public class FibonacciConcurrent {
  public static void main(String[] args) throws InterruptedException {
    // needs to be fair to alternate between threads
    Semaphore semaphore = new Semaphore(1, true);
    // set previous to 1 so that 2nd fibonacci number is correctly calculated to be 0+1=1
    Status initialStatus = new Status(1, 0, 1);
    AtomicReference<Status> statusRef = new AtomicReference<>(initialStatus);
    Fibonacci fibonacci = new Fibonacci(20, semaphore, statusRef);
    Thread thread1 = new Thread(fibonacci);
    Thread thread2 = new Thread(fibonacci);
    thread1.start();
    thread2.start();
    thread1.join();
    thread2.join();
  }

  private static final class Status {
    private final long previous;
    private final long current;
    private final int currentIndex;

    private Status(long previous, long current, int currentIndex) {
      this.previous = previous;
      this.current = current;
      this.currentIndex = currentIndex;
    }
  }

  private static final class Fibonacci implements Runnable {

    private final int target;
    private final Semaphore semaphore;
    private final AtomicReference<Status> statusRef;

    private Fibonacci(int target, Semaphore semaphore, AtomicReference<Status> statusRef) {
      this.target = target;
      this.semaphore = semaphore;
      this.statusRef = statusRef;
    }

    @Override
    public void run() {
      try {
        process();
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new RuntimeException("Interrupted", e);
      }
    }

    private void process() throws InterruptedException {
      while (!Thread.currentThread().isInterrupted()) {
        try {
          semaphore.acquire();
          Status status = statusRef.get();
          String threadName = Thread.currentThread().getName();
          if (status.currentIndex > target) return;
          System.out.println(
              threadName + ": fibonacci number #" + status.currentIndex + " - " + status.current);
          long next = status.previous + status.current;
          Status newStatus = new Status(status.current, next, status.currentIndex + 1);
          statusRef.set(newStatus);
        } finally {
          semaphore.release();
        }
      }
    }
  }
}

将打印:

Thread-0: fibonacci number #1 - 0
Thread-1: fibonacci number #2 - 1
Thread-0: fibonacci number #3 - 1
Thread-1: fibonacci number #4 - 2
Thread-0: fibonacci number #5 - 3

请注意,此解决方案不仅在线程上打印 - 它也在线程上进行实际计算 - 例如当轮到线程 A 时,它使用线程 B 计算的前一个状态来计算下一个斐波那契数。