[英]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();
注意事項(感謝安迪·布朗 ):
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.