繁体   English   中英

如何交替运行 2 个线程

[英]How do I run 2 Threads alternately

我是线程的新手,我正在尝试使 Output 看起来像:aAbBcCdDeE...。 我搜索并尝试了很多东西,但没有任何东西真正起作用。 在练习中,您应该使用全局变量 boolean 来完成它,但我并没有真正找到任何有用的东西并且有点迷路。 感谢您的帮助!

public class ABCThread implements Runnable {
    char c;

    public ABCThread(char c) {
        this.c = c;
    }

    @Override
    public synchronized void run() {

        for (char i = 0; i < 26; i++) {
            System.out.println(c++);
            try {
                Thread.sleep(500);

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

    }

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(new ABCThread('a'));
        Thread t2 = new Thread(new ABCThread('A'));

        t1.start();
        t2.start();

    }

}

Output:

a
A
B
b
C
c
d
D
e
E
f
F
G
g
H
h
I
i
J
j

快速回答:

您不能强制线程在特定时刻运行。 不过,您可以做的是让这两个运行“不同步”(与异步编码无关)。

Thread t1 = new Thread(new ABCThread('a'));
Thread.sleep(250);
Thread t2 = new Thread(new ABCThread('A'));

第一个线程大约每 500 毫秒打印一次,第二个线程将在 250、750、1250 等时打印一次。请注意,这不是在实际程序中解决此问题的正确方法。 但话又说回来,您也可能不会将Thread.sleep(500)添加到 run 方法中。

更好的:

您需要线程之间的同步。 因为你启动了两个线程,所以你可以使用 boolean。

public class ABCThread implements Runnable {
char c;
static boolean synchronizer = false;
boolean runOn;

public ABCThread(char c, boolean runOn) {
    this.c = c;
    this.runOn = runOn;
}

@Override
public synchronized void run() {
    int count = 0;
    while(count < 26)
    {
        if(runOn != synchronizer) 
        {
           Thread.sleep(100);
           continue;
        }
        count++;
        synchronizer = !synchronizer;
        System.out.println(c++);
        
    }

}

    Thread t1 = new Thread(new ABCThread('a', false));
    Thread t2 = new Thread(new ABCThread('A', true));

只需输入此内容。 它可能无法编译。 Thread.sleep(100)只是一些数字。 它可能会更低,但对于这个问题来说并不重要。

更好的是前面的代码可以只用 2 个线程运行。 1 将停留在 synchronizer = false 状态。 如果添加第三个同步器将无法工作,因为它只有 2 个状态。 您可以添加一个 integer 而不是 boolean 和另一个 integer amountOfThreads。 需要将 amountOfThreads 设置为您将启动的线程数量,然后在线程打印时增加原子 integer 并且如果同步器(原子 int)大于 amountOfThreads。 将其设置为 0。

您应该使用boolean来控制哪个线程打印:

  1. 线程 1 等待 boolean 为假。 然后它打印并将其设置为真。
  2. 线程 2 等待 boolean 为真。 然后它打印并将其设置为 false。

这样,线程将交替。 此外,如果您在线程之间使用信号而不是sleep ,所有这些都会快得多:

import java.util.*;
import java.lang.*;
import java.io.*;

public class ABCThread implements Runnable {
    static Object monitor = new Object();
    static boolean turn = false;
    
    char c;
    final boolean myTurn;

    public ABCThread(char c, boolean myTurn) {
        this.c = c;
        this.myTurn = myTurn;
    }

    @Override
    public void run() {

        for (char i = 0; i < 26; i++) {
            synchronized(monitor) {
                while (turn != myTurn) {
                    try {
                        // Note that the lock is release while I'm waiting,
                        // So the other thread can claim it here.
                        monitor.wait();
                    } catch (Exception e) {
                    }
                }
                System.out.println(c++);
                turn = !myTurn;
                monitor.notifyAll();
            }
        }

    }

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(new ABCThread('a', false));
        Thread t2 = new Thread(new ABCThread('A', true));

        t1.start();
        t2.start();
        t1.join();
        t2.join();

    }
}

你需要什么? 线程之间的“共享资源”。

进一步:同步 FSM(有限 state 机器)

下面是演示如何实现该目标的代码。

public class Main
{
    // The shared resource
    public static class State {
        
        private int val;
        private int nStates;

        // Init value and number of states.
        public State(int init, int nStates) {
            this.val = init;
            this.nStates = nStates;
        }
        
        int get() {
            return val;
        }
        // Define how to get to next state. For simple use case, just increase then modulo it. eg: 0, 1, 0, 1
        // Avoid modulo in-case you need very very high performance.
        void next() {
            this.val = (this.val + 1) % nStates;
        }
    }
    
    public static class ABCThread implements Runnable{
        private char c;
        private State s;
        private int type;
        public ABCThread(char c, State s, int type) {
            this.c = c;
            this.s = s;
            this.type = type;
        }

        @Override
        public void run() {
            try {
            for (char i = 0; i < 26; i++) {
                // Do things synchronously
                synchronized(s){
                    while(s.get() != type) {
                        // Wait for our turn.
                        s.wait();
                    }
                    System.out.print(c++);
                    // Update state
                    s.next();
                    // Notify to other threads to do their job.
                    s.notifyAll();
                }
            }
            }catch(Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        // Define the state.
        State s = new State(0, 2);


        /* Uncomment this block to have 3 synchronous threads. Will print aA1bB2...
        s = new State(0, 3);
        Thread t3 = new Thread(new ABCThread('1', s, 2));
        t3.start();
        t3.join();
        */
        Thread t1 = new Thread(new ABCThread('a', s, 0));
        Thread t2 = new Thread(new ABCThread('A', s, 1));


        // Start threads
        t1.start();
        t2.start();
        // Wait for these threads to finish before exit.
        t1.join();
        t2.join();
    }
}

这是解决给定问题的解决方案之一。 它使用wait等待当前线程打印完一个字母后notify另一个线程打印它的字母,如此循环重复。 线程waitnotify的概念在这里得到了很好的解释。

简短的介绍:

waitnotify任何线程,它(调用线程)必须获取任何公共 object 上的锁。在下面的示例中,每个线程正在获取this (自身)(通过同步run方法)的锁以wait并通知opponent (另一个线程)。

public class App implements Runnable {
    char c;
    App opponent;
    boolean go;

    public App(char c, boolean go) {
        this.c = c;
        this.go = go;
    }

    public void setOpponent(App opponent) {
        this.opponent = opponent;
    }

    public static void main(String[] args) {
        App a = new App('a', true);
        App b = new App('A', false);

        Thread t1 = new Thread(a);
        Thread t2 = new Thread(b);

        a.setOpponent(b);
        b.setOpponent(a);

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

    @Override
    public synchronized void run() {
        for (char i = 0; i < 26; i++) {

            try {
                if (go) {
                    System.out.println(c++);
                    this.wait();
                    synchronized (opponent) {
                        opponent.notify();
                    }
                } else {
                    System.out.println(c++);
                    synchronized (opponent) {
                        opponent.notify();
                    }
                    this.wait();
                }

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

这是上述代码的更简化和易于理解的版本。 在这段代码中,两个线程都在获取同一个lock object 上的锁,打印这封信,进入WAIT state(因此释放锁)并转向其他BLOCKED线程等待 lock 上的lock 然后另一个线程在 lock 上获取lock ,打印字母,通知等待lock object 的前一个线程,并进入WAIT状态,从而释放锁。

public class App2 implements Runnable {
    char c;
    Object lock;
    boolean go;

    public App2(char c, boolean go) {
        this.c = c;
        this.go = go;
    }

    public void setLock(Object lock) {
        this.lock = lock;
    }

    public static void main(String[] args) {
        App2 a = new App2('a', true);
        App2 b = new App2('A', false);

        Thread t1 = new Thread(a);
        Thread t2 = new Thread(b);

        Object lock = new Object();

        a.setLock(lock);
        b.setLock(lock);

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

    @Override
    public void run() {

        for (char i = 0; i < 26; i++) {
            synchronized (lock) {
                try {
                    if (go) {
                    
                        System.out.println(c++);
                        lock.wait();
                        lock.notify();
                    
                    } else {
                    
                        System.out.println(c++);
                        lock.notify();
                        lock.wait();
                    
                    }
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

更新:

以上两种方式在大多数情况下都有效,但并不能保证每次t1都先于t2执行。 虽然t1t2之前启动,但这并不意味着线程调度器总是选择t1t2之前执行。 为确保在任何情况下t1都将在t2之前执行,我们需要确保t2t1处于RUNNABLE state(即运行)状态时启动。 以下是我们如何实现它的方法之一:

public class App3 implements Runnable {
    char c;
    App3 opponent;
    boolean go;
    boolean createOpponent = false;

    public App3(char c, boolean go) {
        this.c = c;
        this.go = go;
    }

    public void setOpponent(App3 opponent) {
        this.opponent = opponent;
    }

    public void setCreateOpponent(boolean createOpponent) {
        this.createOpponent = createOpponent;
    }

    public static void main(String[] args) {
        App3 a = new App3('a', true);
        App3 b = new App3('A', false);

        Thread t1 = new Thread(a);

        a.setOpponent(b);
        a.setCreateOpponent(true);

        b.setOpponent(a);

        t1.start();
    }

    @Override
    public synchronized void run() {
        if (createOpponent) {
            setCreateOpponent(false);
            new Thread(opponent).start();
        }
        for (char i = 0; i < 26; i++) {

            try {
                if (go) {
                    System.out.println(c++);
                    this.wait();
                    synchronized (opponent) {
                        opponent.notify();
                    }
                } else {
                    System.out.println(c++);
                    synchronized (opponent) {
                        opponent.notify();
                    }
                    this.wait();
                }

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

希望这可以帮助...

public class ABCThread implements Runnable {
    char c;
    boolean flag;

    public ABCThread(char c, boolean flag) {
        this.c = c;
        this.flag = flag;
    }

    @Override
    public synchronized void run() {
        for (char i = 0; i < 52; i++) {
            try {
                if (flag) {
                    System.out.println(c++);
                    notifyAll();
                    flag = false;
                    wait(100);
                } else {
                    flag = true;
                    wait(100);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(new ABCThread('a', true));
        Thread t2 = new Thread(new ABCThread('A', false));

        t1.start();
        t2.start();

    }
}

或者您可以定义单独的方法来打印小写字母和大写字母,并在两个单独的线程中运行它们

public class ABCThread {

    boolean flag;

    public ABCThread(boolean flag) {
        this.flag = flag;
    }

    public static void main(String[] args) throws InterruptedException {

        ABCThread abc = new ABCThread(false);

        Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    abc.printSmallLetters();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                try {
                    abc.printCapitalLetters();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t1.start();
        t2.start();

    }

    synchronized void printSmallLetters() throws InterruptedException {
        for (char i = 'a'; i < 'z'; i++) {
            while (flag == true) {
                wait();
            }
            System.out.println(i);
            notifyAll();
            flag = true;
        }
    }

    synchronized void printCapitalLetters() throws InterruptedException {
        for (char i = 'A'; i < 'Z'; i++) {
            while (flag == false) {
                wait();
            }
            System.out.println(i);
            notifyAll();
            flag = false;
        }
    }
}

输出

只需将 println 替换为 print 即可获得像这样的系列 output 除了一切似乎都很好,您可以看到我在上面的屏幕截图中得到的 output

决不。 线程的执行顺序无法控制。可以使用Thread.yield()。 但是 yield() - 试图告诉调度程序执行下一个线程,但它停留在 RUNNABLE state。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM