如何来回切换两个线程

时间:2013-03-20 17:22:34

标签: java multithreading synchronisation

我在两个不同的类中有两个方法,比如这个

public class ClassX implements Runnable {

    public  void methodAandB() {
        for(int i=0;i<10;i++) {
            System.out.println("This is A and B ");
        }
    }
    @Override
    public void run() {
        methodAandB();
    }
}

public class ClassY implements Runnable {

    public void methodAorB() {
        for(int i=0;i<10;i++) {
            System.out.println("This is A or B");
        }
    }

    @Override
    public void run() {
        methodAorB(a);
    }
}

线程 t1 正在调用methodAandB()

线程 t2 正在调用methodAorB()


在方法循环的每次迭代后,我可以在这两个线程之间切换吗?

我想得到这样的输出:

  

这是A和B

     

这是A或B

     

这是A和B

     

这是A或B

     

这是A和B

     

这是A或B

     

这是A和B

     

这是A或B

5 个答案:

答案 0 :(得分:1)

线程之间触发器的最佳示例:

给定两个int数组(偶数和奇数),2个线程按自然顺序打印它们的数字。

extern crate tokio_core;
extern crate futures;

use tokio_core::reactor::Core;
use futures::sync::mpsc::unbounded;
use tokio_core::net::TcpListener;
use std::net::SocketAddr;
use std::str::FromStr;
use futures::{Async, Stream, Future, Poll};
use std::thread;
use std::time::Duration;

struct CompletionPact<S, C>
    where S: Stream,
          C: Stream, 
{
    stream: S,
    completer: C,
}

fn stream_completion_pact<S, C>(s: S, c: C) -> CompletionPact<S, C>
    where S: Stream,
          C: Stream,
{
    CompletionPact {
        stream: s,
        completer: c,
    }
}

impl<S, C> Stream for CompletionPact<S, C>
    where S: Stream,
          C: Stream,
{
    type Item = S::Item;
    type Error = S::Error;

    fn poll(&mut self) -> Poll<Option<S::Item>, S::Error> {
        match self.completer.poll() {
            Ok(Async::Ready(None)) |
            Err(_) |
            Ok(Async::Ready(Some(_))) => {
                // We are done, forget us
                Ok(Async::Ready(None)) // <<<<<< (3)
            },
            Ok(Async::NotReady) => {
                self.stream.poll()
            },
        }
    }
}

fn main() {
    // unbounded() is the equivalent of a Stream made from a channel()
    // directly create it in this thread instead of receiving a Sender
    let (tx, rx) = unbounded::<()>();
    // A second one to cause forgetting the listener
    let (l0tx, l0rx) = unbounded::<()>();

    let j = thread::spawn(move || {
        let mut core = Core::new().unwrap();

        // Listener-0
        {
            let l = TcpListener::bind(
                    &SocketAddr::from_str("127.0.0.1:44444").unwrap(),
                    &core.handle())
                .unwrap();

            // wrap the Stream of incoming connections (which usually doesn't
            // complete) into a Stream that completes when the
            // other side is drop()ed or sent on
            let fe = stream_completion_pact(l.incoming(), l0rx)
                .for_each(|(_sock, peer)| {
                    println!("Accepted from {}", peer);
                    Ok(())
                })
                .map_err(|e| println!("----- {:?}", e));

            core.handle().spawn(fe);
        }

        // Listener1
        {
            let l = TcpListener::bind(
                    &SocketAddr::from_str("127.0.0.1:55555").unwrap(),
                    &core.handle())
                .unwrap();

            let fe = l.incoming()
                .for_each(|(_sock, peer)| {
                    println!("Accepted from {}", peer);
                    Ok(())
                })
                .map_err(|e| println!("----- {:?}", e));

            core.handle().spawn(fe);
        }

        let _ = core.run(rx.into_future());
        println!("Exiting event loop thread");
    });

    thread::sleep(Duration::from_secs(2));
    println!("Want to terminate listener-0");
    // A drop() will result in the rx side Stream being completed,
    // which is indicated by Ok(Async::Ready(None)).
    // Our wrapper behaves the same when something is received.
    // When the event loop encounters a
    // Stream that is complete it forgets about it. Which propagates to a
    // drop() that close()es the file descriptor, which closes the port if
    // nothing else uses it.
    l0tx.send(()).unwrap(); // alternatively: drop(l0tx);
    // Note that this is async and is only the signal
    // that starts the forgetting.

    thread::sleep(Duration::from_secs(2));
    println!("Want to exit event loop");
    // Same concept. The reception or drop() will cause Stream completion.
    // A completed Future will cause run() to return.
    tx.send(()).unwrap();

    j.join().unwrap();
}

答案 1 :(得分:0)

这可能不仅仅是解决问题所需要的,但是,由于它似乎是对并发编程练习的介绍,它应该与你将遇到的一致。

你应该有一个你的线程都知道的共享对象,以便它们可以通过它同步。像这样:

public class MyMutex {
    private int whoGoes;
    private int howMany;

    public MyMutex(int first, int max) {
        whoGoes = first;
        howMany = max;
    }

    public synchronized int getWhoGoes() { return whoGoes; }

    public synchronized void switchTurns() {
        whoGoes = (whoGoes + 1) % howMany;
        notifyAll();
    }

    public synchronized void waitForMyTurn(int id) throws
            InterruptedException {
        while (whoGoes != id) { wait(); }
    }
}

<小时/> 现在,您的类应该接收它们各自的标识符和这个共享对象。

public class ClassX implements Runnable {
    private final int MY_ID;
    private final MyMutex MUTEX;

    public ClassX(int id, MyMutex mutex) {
        MY_ID = id;
        MUTEX = mutex;
    }

    public void methodAandB() {
        for(int i = 0; i < 10; i++) {
            try {
                MUTEX.waitForMyTurn(MY_ID);
                System.out.println("This is A and B ");
                MUTEX.switchTurns();
            } catch (InterruptedException ex) {
                // Handle it...
            }
        }
    }
    @Override
    public void run() { methodAandB(); }
}

ClassY应该这样做。等待轮到你,行动,然后转向另一个。

答案 2 :(得分:0)

您只需使用共享变量即可实现此目的。我已经实施并验证了这个问题。代码在下面

X级

public class ClassX implements Runnable {
public  void methodAandB() {
    for(int i=0;i<10;i++) {
        while(GlobalClass.isClassXdone)
        {}
        System.out.println("This is A and B ");
        GlobalClass.isClassXdone = true;
        GlobalClass.isClassYdone = false;
}}
@Override
public void run() {
    methodAandB(); } }

<强>优雅

public class ClassY implements Runnable {
public  void methodAorB() {
    for(int i=0;i<10;i++) {
         while(GlobalClass.isClassYdone)
         {}
        System.out.println("This is A or B ");
        GlobalClass.isClassYdone = true;
        GlobalClass.isClassXdone = false;}}
@Override
public void run() {
    methodAorB();}}

共享变量的定义

public class GlobalClass {

public  static boolean isClassXdone = false ;
public  static boolean isClassYdone = false ;
}

您可以使用t1.start和t2.start启动线程以获得所需的输出

    Thread t1 = new Thread(new ClassX());
    Thread t2 = new Thread(new ClassY());
    t1.start();
    t2.start();

答案 3 :(得分:0)

我知道回答这个问题有点晚了。但昨天只有我遇到过这个问题。所以我想这永远不会太晚..;)

解决方案,正如@afsantos所提到的那样,两个线程之间有一个共享对象,并在共享对象上实现互斥。共享对象也可以由两个线程锁定。两种可能的实现如下。这实际上更像是@afsantos解决方案的扩展。特此承认他的工作。

解决方案1 ​​: 将共享的对象的蓝图如下。

public class MutEx {
    public int whoGoes, howMany;

    public MutEx(int whoGoes, int howMany) {
        this.whoGoes = whoGoes;
        this.howMany = howMany;
    }

    public synchronized void switchTurns(){
        this.whoGoes = (this.whoGoes + 1) % 2;
        notifyAll();
    }

    public synchronized void waitForTurn(int id) throws InterruptedException{
        while(this.whoGoes != id)
            wait();
    }
}

然后,您可以按如下方式实现ClassX。

public class ClassX implements Runnable {
    private final int MY_ID;
    private final MutEx MUT_EX;

    public ThreadOne(int MY_ID, MutEx MUT_EX) {
        this.MY_ID = MY_ID;
        this.MUT_EX = MUT_EX;
    }

    @Override
    public void run(){
        this.doTheWork();
    }

    public void doTheWork(){
        for(int i = 0; i < 10; i++){
            try {
                MUT_EX.waitForMyTurn(MY_ID);
                System.out.println("This is A and B");
                MUT_EX.switchTurns();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

ClassY也将是相同的,无论你需要在那里有什么不同。然后,在调用中(即在主方法中),

public static void main(String[] args) {
    MutEx mutEx = new MutEx(0, 2);
    Thread t1 = new Thread(new ClassX(0, mutEx);
    Thread t2 = new Thread(new ClassY(1, mutEx));
    t1.start();
    t2.start();
}

瞧!你有两个线程,根据需要在每个线程之间交替。

解决方案2 :或者,您可以实施ClassX&amp; ClassY如下。

public class ClassX extends Thread{

在这里,您是java.lang.Thread的子类,以实现您的要求。要调用它,请按如下所示更改main方法。

public static void main(String[] args) {
    MutEx mutEx = new MutEx(0, 2);
    ClassX t1 = new ClassX(0, mutEx);
    ClassY t2 = new ClassY(1, mutEx);
    t1.start();
    t2.start();
}

运行此命令,结果相同。

答案 4 :(得分:-3)

如果您不需要使用Thread,请尝试以下代码:

for (int i = 0; i < 20; i++) {
    if (i % 2 == 0) {
        methodAandB();
    } else {
        methodAorB();
    }
}