创建两个线程,一个显示奇数,另一个显示偶数。

22

我正在尝试创建两个线程,一个线程显示从0到10的偶数,另一个线程显示从1到11的奇数。以下代码是否适合设计此程序?

public class Mythread {

    public static void main(String[] args) {
        Runnable r = new Runnable1();
        Thread t = new Thread(r);
        t.start();
        Runnable r2 = new Runnable2();
        Thread t2 = new Thread(r2);
        t2.start();
    }
}

class Runnable2 implements Runnable{
    public void run(){
        for(int i=0;i<11;i++){
            if(i%2 == 1)
                System.out.println(i);
        }
    }
}

class Runnable1 implements Runnable{
    public void run(){
        for(int i=0;i<11;i++){
            if(i%2 == 0)
                System.out.println(i);
        }
    }
}

它是否能够满足你的需求?如果是的话,那我想它就是合适的... - ramsinb
如果你正在比较线程运行的方式/时间,最好在彼此之后立即调用Thread.start()方法。将“t.start();”移动到“t2.start();”正上方。 - mcalex
@Frank 是的,我运行了它,它可以正常工作,但是不确定这是否是最佳实现方式。 - Bernard
21个回答

46

@aymeric的答案不能按照自然顺序打印出数字,但是这段代码可以。解释请见结尾。

public class Driver {
    static Object lock = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            public void run() {

                for (int itr = 1; itr < 51; itr = itr + 2) {
                    synchronized (lock) {
                        System.out.print(" " + itr);
                        try {
                            lock.notify();
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            public void run() {

                for (int itr = 2; itr < 51; itr = itr + 2) {
                    synchronized (lock) {
                        System.out.print(" " + itr);
                        try {
                            lock.notify();
                            if(itr==50)
                                break;
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
        try {
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            System.out.println("\nPrinting over");
        } catch (Exception e) {

        }
    }
}
为了实现这一点,上面两个线程的run方法必须依次调用,即它们需要被同步,我使用锁来实现这一点。
代码的工作方式如下:t1.run打印奇数并通知任何等待的线程它将释放锁,然后进入等待状态。
此时调用t2.run,它打印下一个偶数,通知其他线程它将释放所持有的锁,然后进入等待状态。
这将继续进行,直到t2.run中的itr达到50,此时我们的目标已经实现,需要停止这两个线程。
通过break,我避免了在t2.run中调用lock.wait(),因此关闭了t2线程,控制权现在转移到t1.run,因为它正在等待获取锁;但在这里 itr 值将是> 51,我们将退出其run(),从而关闭线程。
如果在t2.run()中不使用 break,尽管我们将在屏幕上看到数字1到50,但两个线程将陷入死锁状态并继续处于等待状态。

1
你能否再解释一下你的代码,加上注释。 - Whitecat
1
我的意思是在答案中添加注释,而不是在底部。这将有所帮助。 - Whitecat
1
t1会始终先启动吗? - Praveen Kumar
2
如何保证t1先运行? - rd22
仅在t1线程中等待并在t2中通知不足够吗?为什么我们需要在t1和t2中都使用wait和notify? - Govind Prabhu
显示剩余2条评论

15
我只需要更改一些细节(这里不需要使用模运算符...):
public class Mythread {

    public static void main(String[] args) {
        Runnable r = new Runnable1();
        Thread t = new Thread(r);
        Runnable r2 = new Runnable2();
        Thread t2 = new Thread(r2);
        t.start();
        t2.start();
    }
}

class Runnable2 implements Runnable{
    public void run(){
        for(int i=0;i<11;i+=2) {
            System.out.println(i);
        }
    }
}

class Runnable1 implements Runnable{
    public void run(){
        for(int i=1;i<=11;i+=2) {
           System.out.println(i);
        }
    }
}

请原谅我问一个愚蠢的问题,但在这种情况下是否有可能进行上下文切换?即使线程尚未完成,奇数线程也有机会运行,依此类推。 - Rajeev Akotkar
1
这是一个非常实用和简单的例子,非常容易理解。我只是尝试跟随这个例子的模式,并成功地将线程应用到我的大项目中。 :) 谢谢这个例子。 - heisenberg
它不保持数字的自然顺序。 - Ram

4

没问题。但在这种情况下,我认为你不需要两个线程,因为操作很简单。不过,如果你想练习使用线程,也可以。


3
说实话,这是考试中的一个问题。我希望我的老师也像你一样思考。 - Bernard
是的,他会这样想。如果他没有这样想,你可以说,你已经以“可调整的方式”开发它 :D。这意味着,如果您要使用1000万个数字执行相同的任务,这就是答案 :D。 - PeakGen

3
package javaapplication45;

public class JavaApplication45 extends Thread {

    public static void main(String[] args) {
        //even numbers
        Thread t1 = new Thread() {
            public void run() {
                for (int i = 1; i <= 20; i++) {
                    if (i % 2 == 0) {
                        System.out.println("even thread " + i);
                    }
                }
            }
        };
        t1.start();
        //odd numbers
        Thread t2 = new Thread() {
            public void run() {
                for (int i = 1; i <= 20; i++) {
                    if (i % 2 != 0) {
                        System.out.println("odd thread " + i);
                    }
                }
            }
        };
        t2.start();

    }

}

3

以下是使用锁定共享对象的代码,该对象具有要打印的数字。与上面的解决方案不同,它可以保证顺序。

public class MultiThreadPrintNumber {
  int i = 1;

  public synchronized void printNumber(String threadNm) throws InterruptedException{

      if(threadNm.equals("t1")){
        if(i%2 == 1){
          System.out.println(Thread.currentThread().getName()+"--"+ i++);
          notify();
        } else {
          wait();
        }
      } else if(threadNm.equals("t2")){
        if(i%2 == 0){
          System.out.println(Thread.currentThread().getName()+"--"+ i++);
          notify();
        } else {
          wait();
        }
      }
      
    }

  public static void main(String[] args) {
    final MultiThreadPrintNumber obj = new MultiThreadPrintNumber();
    Thread t1 = new Thread(new Runnable() {

      @Override
      public void run() {
        try {
          while(obj.i <= 10){
            
            obj.printNumber(Thread.currentThread().getName());
          }
        } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        System.out.println("done t1");
      }
    });
    Thread t2 = new Thread(new Runnable() {

      @Override
      public void run() {
        try {
          while(obj.i <=10){
            obj.printNumber(Thread.currentThread().getName());
          }
        } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        System.out.println("done t2");
      }
    });

    t1.setName("t1");
    t2.setName("t2");
    t1.start();
    t2.start();
  }
}

输出结果将如下所示:
t1--1
t2--2
t1--3
t2--4
t1--5
t2--6
t1--7
t2--8
t1--9
t2--10
done t2
done t1

1
Main class
===========
package com.thread;

import java.util.concurrent.atomic.AtomicInteger;

public class StartThread {
    static AtomicInteger no = new AtomicInteger(1);
    public static void main(String[] args) {
        Odd oddObj = new Odd();
        Thread odd = new Thread(oddObj);
        Thread even = new Thread(new Even(oddObj));
        odd.start();
        even.start();
    }
}

Odd Thread
===========
package com.thread;

public class Odd implements Runnable {

    @Override
    public void run() {
        while (StartThread.no.get() < 20) {
            synchronized (this) {
                System.out.println("Odd=>" + StartThread.no.get());
                StartThread.no.incrementAndGet();

                try {
                    this.notify();
                    if(StartThread.no.get() == 20)
                        break;
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}

Even Thread
===========
package com.thread;

public class Even implements Runnable {
    Odd odd;

    public Even(Odd odd) {
        this.odd = odd;
    }

    @Override
    public void run() {
        while (StartThread.no.get() < 20) {
            synchronized (odd) {
                System.out.println("Even=>" + StartThread.no.get());
                StartThread.no.incrementAndGet();
                odd.notifyAll();
                try {
                    odd.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }       

    }

}

Output (Nos are printed in sequential)
======
Odd=>1
Even=>2
Odd=>3
Even=>4
Odd=>5
Even=>6
Odd=>7
Even=>8
Odd=>9
Even=>10
Odd=>11
Even=>12
Odd=>13
Even=>14
Odd=>15
Even=>16
Odd=>17
Even=>18
Odd=>19

1

并发包:

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    //=========== Task1 class prints odd =====
    class TaskClass1 implements Runnable
    {

    private Condition condition;
    private Lock lock;
    boolean exit = false;
    int i;
    TaskClass1(Condition condition,Lock lock)
    {
        this.condition = condition;
        this.lock = lock;
    }
    @Override
    public void run() {
        try
        {
            lock.lock();
            for(i = 1;i<11;i++)
            {
                if(i%2 == 0)
                {
                    condition.signal();
                    condition.await();

                }
                if(i%2 != 0)
                {
                    System.out.println(Thread.currentThread().getName()+" == "+i);

                }

            }

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally
        {
            lock.unlock();
        }

    }

}
//==== Task2 : prints even =======
class TaskClass2 implements Runnable
{

    private Condition condition;
    private Lock lock;
    boolean exit = false;
    TaskClass2(Condition condition,Lock lock)
    {
        this.condition = condition;
        this.lock = lock;
    }
    @Override
    public void run() {
        int i;
        // TODO Auto-generated method stub
        try
        {
            lock.lock();
            for(i = 2;i<11;i++)

            {

                if(i%2 != 0)
                {
                    condition.signal();
                    condition.await();
                }
                if(i%2 == 0)
                {
                    System.out.println(Thread.currentThread().getName()+" == "+i);

                }

            }

        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally
        {
            lock.unlock();

        }

    }

}
public class OddEven {

    public static void main(String[] a)
    {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        Future future1;
        Future future2;
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        future1 = executorService.submit(new TaskClass1(condition,lock));
        future2 = executorService.submit(new TaskClass2(condition,lock));
        executorService.shutdown();


    }


}

1

虽然不是上述问题的答案,但与其类似。

编写程序按顺序打印数组元素,但使用两个不同的线程来打印相邻的元素。

import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author ntv
 */
public class PrintLAternateNumber {
    public static void main(String[] args) {
        int [] num = {1,2,3,4,5,6};
        Printer p = new Printer();
        Thread t1 = new Thread(new Thread1(num,  p), "Thread1");
        Thread t2 = new Thread(new Thread2(num,  p), "Thread2");
        t1.start();
        t2.start();
    }


}

class Thread1 implements Runnable {
    int [] num;
    Printer p ;
    public Thread1(int[] num,  Printer p) {
        this.num = num;
        this.p = p;
    }

    public void run() {
        try {
            print();
        } catch (InterruptedException ex) {
            Logger.getLogger(Thread1.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void print() throws InterruptedException {
        int i = 1;

            while(i < num.length) {
                synchronized(num) {
                    while (p.evenPrinted) {
                        num.wait();
                     }
                }   

                synchronized(num) {
                     p.printEven(Thread.currentThread().getName(), num[i]);
                     i= i + 2;
                     num.notifyAll();

                }
            }
    }
}


class Thread2 implements Runnable {
    int [] num;
    Printer p ;
    public Thread2(int[] num, Printer p) {
        this.num = num;
        this.p = p;
    }

    public void run() {
        try {
            print();
        } catch (InterruptedException ex) {
            Logger.getLogger(Thread2.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void print() throws InterruptedException {
        int i = 0;

            while(i < num.length) {
                synchronized(num) {
                    while (!p.evenPrinted) {
                        num.wait();
                     }
                }    

                 synchronized(num) {
                     p.printOdd(Thread.currentThread().getName(), num[i]);
                     i = i + 2;
                     num.notifyAll();

                }
            }
    }
}

class Printer {
    boolean evenPrinted = true;
    void printEven(String threadName , int i) {
        System.out.println(threadName + "," + i);
        evenPrinted = true;
    }


        void printOdd(String threadName , int i) {
        System.out.println(threadName + "," + i);
        evenPrinted = false;
    }
}

1
package thread;

import org.hibernate.annotations.Synchronize;

class PrintOdd implements Runnable {
    int count = -1;
    private Object common;

    PrintOdd(Object common) {
        this.common = common;
    }

    @Override
    public void run() {
        synchronized (common) {
            while (count < 1000) {
                try {
                    common.notifyAll();
                    System.out.println(count += 2);
                    common.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

    }

}

class PrintEven implements Runnable {

    int count = 0;
    private Object common;

    PrintEven(Object common) {
        this.common = common;
    }

    @Override
    public void run() {
        synchronized (common) {
            while (count < 1000) {
                try {
                    common.notifyAll();
                    System.out.println(count += 2);
                    common.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

    }

}

public class PrintNatural {
    public static void main(String args[]) {
        Object obj = new Object();
        Runnable r = new PrintOdd(obj);
        Thread printOdd = new Thread(r);

        Runnable r2 = new PrintEven(obj);
        Thread printEven = new Thread(r2);

        printOdd.start();
        printEven.start();

    }

}

1

如果你想要其他选择,我也会考虑使用Java Concurrency。Java Concurrency包中提供的一些功能比直接使用Thread类提供了更高级别的抽象,并因此提供了更多的功能。

对于你的特定情况,你所做的是相当合理的,但这些数字的打印顺序重要吗?你想要先输出偶数还是奇数?这些问题将更好地指示最适合你需求的设计。


谢谢您的回复。如果我想让每个线程依次发生,怎么办?我的意思是一个奇数一个偶数一个奇数一个偶数等等。 - Bernard
1
在这种情况下,我不会费心去使用线程。你为什么认为需要使用线程呢?通常情况下,如果你想要并行处理、跨进程分发功能等,才会使用线程。 - ramsinb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接