簡體   English   中英

帶有多線程Java的Hello World

[英]Hello World with Multithreading Java

我想了解如何使用關鍵字:等待,通知/全部,已同步,因此我決定嘗試一個簡單的示例。 基本上我想做的是創建兩個將打印字符串的線程。 第一個線程的字符串為“ Hello”,第二個線程的字符串為“ World”。

我想要的輸出如下:Hello World Hello World Hello World ...

這是我到目前為止編寫的代碼,但是現在的輸出是:Hello Hello Hello ...世界世界世界...

錯誤在哪里? 謝謝。 :)

這是代碼:

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();
    }
}

您在這里拿着鎖,所以一次只能打印一個進程

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

您可以使用以下方法暫時解除鎖定:

   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);
            }
        }
    }

您可能想到的是我所說的乒乓球測試。 您不會在真正的程序中執行此操作,但是此模式可提供有用的微基准。

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");

    }
}

版畫

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

我猜您想用兩個訪問相同資源的獨立線程來模擬一個作業:例如System.out,但是睡眠部分可能可以同時運行。

在仿真中,不應將暫停放在同步塊中:

public class SimpleSyncThread {

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

在運行功能中,您不再需要同步:

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

現在,您將獲得“ Hello World Hello World”或“ Hello World World Hello”。

您已經完成了一個具有不小的困難的示例:java中通過wait and notify的基本同步旨在同步使用者-生產者范例:有一些生產者線程和一些使用者線程。 每個生產者無需等待便完成其工作,然后喚醒(通知)正在等待通知的使用者線程。 即使可運行類同時是消費者和生產者,此方案也將起作用。 但是通信始終是單向的

   producer -> consumer

相反,您嘗試做的是雙向替代線程通信:

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

我認為您需要一種更復雜的方式來與線程進行通信: 令牌管理器 ,該類包含一個從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;
    }
}

然后是一個可運行的類,該類接收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();
}

這樣,是通過將ID分配給實例化線程(升序)來決定激活順序的主要方法。 而且,如果您想要一種新型的線程,則只需將3傳遞給TokenManager(而不是2 )並使用適當的ID啟動一個新的線程:

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

注意事項(感謝安迪·布朗 ):

  • 令牌ID必須按順序無間隙地給出。
  • 具有相同令牌ID的線程可能不止一個。 在這種情況下,它們將在輪到自己的位置隨機執行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM