简体   繁体   English

带有多线程Java的Hello World

[英]Hello World with Multithreading Java

I'm tying to understand how to use the keywords: wait, notify/All, synchronized, so I decided to try with a simple example. 我想了解如何使用关键字:等待,通知/全部,已同步,因此我决定尝试一个简单的示例。 Basically what I'm trying to do is to create two threads that are going to print a string. 基本上我想做的是创建两个将打印字符串的线程。 The first thread has the string "Hello", while the second thread has the String "World". 第一个线程的字符串为“ Hello”,第二个线程的字符串为“ World”。

The output I'd like to reach is the following: Hello World Hello World Hello World ... 我想要的输出如下:Hello World Hello World Hello World ...

This is the code I wrote so far, but the output right now is: Hello Hello Hello ... World World World ... 这是我到目前为止编写的代码,但是现在的输出是:Hello Hello Hello ...世界世界世界...

Where is/are the mistake/s? 错误在哪里? Thank you. 谢谢。 :) :)

Here's the code: 这是代码:

class MyThread implements Runnable {
    private SimpleSyncThread sync;
    private String s;

    public MyThread(SimpleSyncThread sync, String s) {
        this.sync = sync;
        this.s = s;
    }

    public static void pause(long time) {
        try {Thread.sleep(time); }
        catch (InterruptedException e) {Thread.currentThread().interrupt();}
    }

    @Override
    public void run() {
        synchronized (sync) {
            for (int i = 0; i < 10; i++) {
                sync.print(s);
            }
        }
    }
}

public class SimpleSyncThread {

    public void print(String s) {
        System.out.println(s);
        MyThread.pause(200);
    }

    public static void main(String[] args) {
        SimpleSyncThread sync = new SimpleSyncThread();
        MyThread st1 = new MyThread(sync, "Hello");
        MyThread st2 = new MyThread(sync, "World");

        Thread t1 = new Thread(st1);
        Thread t2 = new Thread(st2);

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

You are holding the lock here so only one process can print at a time 您在这里拿着锁,所以一次只能打印一个进程

   synchronized (sync) {
        for (int i = 0; i < 10; i++) {
            sync.print(s);
        }
    }

Instead of doing this you can release the lock temporarily with 您可以使用以下方法暂时解除锁定:

   synchronized (sync) {
        for (int i = 0; i < 10; i++) {
            sync.print(s);
            // I have done my bit, wake other threads.
            sync.notifyAll();
            try {
                // give up the lock and let another thread run.
                sync.wait(10);
            } catch(InterruptedException ie) {
                throw new AssertionError(ie);
            }
        }
    }

What you might have had in mind is what I call a Ping Pong test. 您可能想到的是我所说的乒乓球测试。 You wouldn't do this in a real program but this pattern makes a useful micro-benchmark. 您不会在真正的程序中执行此操作,但是此模式可提供有用的微基准。

public class PingPongMain {
    public static void main(String[] args) throws InterruptedException {
        boolean[] next = {false};
        AtomicInteger count = new AtomicInteger();
        Thread t1 = new Thread(() -> {
            try {
                synchronized (next) {
                    for(;;) {
                        // handle spurious wake ups.
                        while (next[0])
                            next.wait();

                        System.out.println("ping");

                        // state change before notify
                        next[0] = true;
                        next.notifyAll();
                    }
                }
            } catch (InterruptedException e) {
                // expected
            }
        });

        Thread t2 = new Thread(() -> {
            try {
                synchronized (next) {
                    for(;;) {
                        // handle spurious wake ups.
                        while (!next[0])
                            next.wait();

                        System.out.println("pong");

                        // state change before notify
                        next[0] = false;
                        next.notifyAll();

                        count.incrementAndGet();
                    }
                }
            } catch (InterruptedException e) {
                // expected
            }
        });

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

        Thread.sleep(5000);
        t1.interrupt();
        t2.interrupt();
        System.out.println("Ping ponged " + count + " times in 5 seconds");

    }
}

prints 版画

ping
pong
ping
pong
.. deleted ...
Ping ponged 323596 times in 5 seconds

I guess you want to simulate a job with two independent thread accessing the same resource: eg the System.out, but the sleep parts can probably run concurrently. 我猜您想用两个访问相同资源的独立线程来模拟一个作业:例如System.out,但是睡眠部分可能可以同时运行。

In your simulation, you shouldn't put the pause inside the synchronized block: 在仿真中,不应将暂停放在同步块中:

public class SimpleSyncThread {

    public void print(String s) {
        synchronized(this){
           System.out.println(s);
        }
        MyThread.pause(200);
    }

In the run function, you don't need the synchronized anymore: 在运行功能中,您不再需要同步:

public void run() {
        for (int i = 0; i < 10; i++) {
            sync.print(s);
        }
}

Now, you'll get 'Hello World Hello World', or maybe 'Hello World World Hello'. 现在,您将获得“ Hello World Hello World”或“ Hello World World Hello”。

You have gone on an example with a non small amount of difficulty: The basic synchronization in java through wait and notify is aimed to synchronize a consumer-producer paradigm: There are some producer threads and some consumer threads. 您已经完成了一个具有不小的困难的示例:java中通过wait and notify的基本同步旨在同步使用者-生产者范例:有一些生产者线程和一些使用者线程。 Each producer does its job without waiting, and then wakes up (notify) a consumer thread, which is waiting for a notification. 每个生产者无需等待便完成其工作,然后唤醒(通知)正在等待通知的使用者线程。 This scheme would serve even if the runnable class is simultaneosly consumer and producer. 即使可运行类同时是消费者和生产者,此方案也将起作用。 But the communication is always unidirectional : 但是通信始终是单向的

   producer -> consumer

Instead, what you try to do is bidirectional alternative thread communication: 相反,您尝试做的是双向替代线程通信:

   producer -> consumer -> producer -> consumer ...

I think you need a more complicated way to communicate your threads: A token manager , a class that contains an integer token from 0 to N, and rotates it: 我认为您需要一种更复杂的方式来与线程进行通信: 令牌管理器 ,该类包含一个从0到N的整数令牌,并将其旋转:

public class TokenManager
{
    private final int maxTokens;

    private int token;

    public TokenManager(int maxTokens, int initialToken)
    {
        super();
        this.maxTokens=maxTokens;
        this.token=initialToken % this.maxTokens;
    }

    public int getToken()
    {
        return this.token;
    }

    public void nextToken()
    {
        this.token=++this.token % this.maxTokens;
    }
}

And then a runnable class that receives a TokenManager and uses it for synchronization: 然后是一个可运行的类,该类接收TokenManager并将其用于同步:

public class MyRunnable implements Runnable
{
    private final String text;

    private final TokenManager token;

    // Identifier token value for this object.
    private final int id;

    public MyRunnable(int id, String text, TokenManager token)
    {
        super();
        this.id=id;
        this.text=text;
        this.token=token;
    }

    @Override
    public void run()
    {
        try
        {
            for (int i=0; i < 10; i++)
            {
                synchronized (this.token)
                {
                    // Wait until the TokenManager token is equal to the id of this object:
                    while (this.token.getToken() != this.id)
                    {
                        this.token.wait();
                    }

                    // Now it's our turn to print:
                    System.out.printf("%d: %s\n", i, this.text);
                    this.token.nextToken();

                    // Ask the TokenManager to progress to the next token:
                    this.token.notifyAll();
                }
            }
        }
        catch (InterruptedException e)
        {
            throw new Error(e);
        }
    }
}

public static void main(String[] args)
{
    // Instantiate the TokenManager for a specified number of threads:
    TokenManager token=new TokenManager(2, 0);

    // Instantiate and start the thread with id=0:
    new Thread(new MyRunnable(0, "Hello", token)).start();
    // Instantiate and start the thread with id=1:
    new Thread(new MyRunnable(1, "World", token)).start();
}

In this way, it is the main method who decides what will be the sequence of activation, by assigning IDs to the instantiated threads (in ascending order). 这样,是通过将ID分配给实例化线程(升序)来决定激活顺序的主要方法。 And, if you want a new type of thread, you'll have just to pass 3 to the TokenManager (instead of 2 ) and start a new Thread with a proper ID: 而且,如果您想要一种新型的线程,则只需将3传递给TokenManager(而不是2 )并使用适当的ID启动一个新的线程:

    new Thread(new MyRunnable(2, "I'm here", token)).start();

Notes (Thanks to Andy Brown ): 注意事项(感谢安迪·布朗 ):

  • The token IDs must be given sequentially with no gaps. 令牌ID必须按顺序无间隙地给出。
  • There could be more than one Thread with the same Token ID. 具有相同令牌ID的线程可能不止一个。 In this case, they would be executed randomly within their turn. 在这种情况下,它们将在轮到自己的位置随机执行。

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

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